#779 – Methods in struct that Modify Elements Can Be Dangerous

If you have a method in a struct that modifies a data member of the struct, you can run into unexpected results, due to the value type semantics of the struct.

Assume that we have a struct that includes a method that can change the value of one of its data members.  The method works just fine for a locally defined instance of the struct.

            // Method that modifies struct works if local
            DogCollarInfo collar = new DogCollarInfo(0.5, 8.0);
            collar.Dump();
            collar.DoubleLength();
            collar.Dump();

779-001
But if you have a property of some class whose type is this struct, it’s no longer safe to call this method. Because the property’s get accessor returns a copy of the struct, the data in the original struct won’t get modified.

            // Does not work if struct is property
            Dog d = new Dog("Kirby");
            d.Collar = new DogCollarInfo(0.5, 8.0);
            d.Collar.Dump();
            d.Collar.DoubleLength();
            d.Collar.Dump();   // Length not doubled!

779-002

Advertisement

#767 – A struct Is Implicitly Sealed

Every struct in C#, whether it is user-defined or defined in the .NET Framework, is sealed–meaning that you can’t inherit from it.  A struct is sealed because it is a value type and all value types are sealed.

A struct can implement an interface, so it’s possible to see another type name following a colon, after the name of the struct.

In the example below, we get a compile-time error when we try to define a new struct that inherits from the one defined above.

    public struct PersonName
    {
        public PersonName(string first, string last)
        {
            First = first;
            Last = last;
        }

        public string First;
        public string Last;
    }

    // Error at compile time: Type 'PersonName' in interface list is not an interface
    public struct AngryPersonName : PersonName
    {
        public string AngryNickname;
    }

#411 – Overriding the Equals Method for a Value Type

A user-defined struct inherits from System.ValueType implicitly.

System.ValueType includes a default implementation of the Equals method that compares the individual fields of two instances of the type, to determine if the instances are equivalent.  It uses reflection to determine which fields to compare and compares both public and private fields.  (This includes the values of auto-implemented properties, which use an automatically generated private backing field).

You should generally override the Equals method in a user-defined struct, comparing all of the fields directly.  This is typically faster than the default implementation, since your method does not need to use reflection.

If you are also overloading the == operator, you can define Equals in terms of the operator.

        public override bool Equals(object obj)
        {
            return ((obj is PersonHeight) && ((PersonHeight)obj == this));
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }