#1,220 – C# 6.0 – Defining a Parameterless Constructor for a struct

In C# 5.0, every struct had a default parameterless constructor that you couldn’t override.  Using the new operator invoked this constructor and all members of the struct were assigned default values.

    public struct MyPoint
    {
        public double X;
        public double Y;
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyPoint pt = new MyPoint();      // default values for X, Y (0.0)
            Console.WriteLine("{0}, {1}", pt.X, pt.Y);
        }
    }

1220-002

In C# 6.0, you can explicitly define a parameterless constructor for a struct, giving non-default values to the members of the struct.

        public struct MyPoint
        {
            public double X;
            public double Y;

            public MyPoint()
            {
                X = 100.0;
                Y = 100.0;
            }
        }

        static void Main(string[] args)
        {
            MyPoint pt = new MyPoint();      // 100.0, 100.0
            Console.WriteLine("{0}, {1}", pt.X, pt.Y);
        }

1220-001

Advertisements

#782 – You Can Create an Instance of a struct Without the new Keyword

Like a class, you can create an instance of a struct using the new keyword.  You can either invoke the default parameterless constructor, which initializes all fields in the struct to their default values, or you can invoke a custom constructor.

            // Method 1 - Parameterless constructor, data in struct initialized to default values
            DogCollar collar = new DogCollar();

            // Method 2 - Call custom constructor
            DogCollar collar2 = new DogCollar(10.0, 0.5);

In either case, a constructor is called.

You can also create an instance of a struct by just declaring it, without using the new operator.  The object is created, although you can’t use it until you’ve explicitly initialized all of its data members.

            // Method 3 - Just declare it
            DogCollar collar3;

            // Error - Can't use collar3 yet (use of unassigned field)
            Console.WriteLine(collar3.Length);

We need to first initialize all data members:

            // Correct - initialize first
            DogCollar collar3;
            collar3.Length = 10.0;
            collar3.Width = 5.0;
            Console.WriteLine(collar3.Length);

#781 – A struct Can Implement an Interface

Like a class, a struct can implement an interface.  In the example below, the DogCollar struct implements the IStrapDimensions interface, which contains a couple of properties and a method.

    public interface IStrapDimensions
    {
        double Length { get; }
        double Width { get; }

        double CalcArea();
    }

    public struct DogCollar : IStrapDimensions
    {
        private double length;
        public double Length
        {
            get { return length; }
        }

        private double width;
        public double Width
        {
            get { return width; }
        }

        public double CalcArea()
        {
            return Length * Width;
        }

        public DogCollar(double len, double wid)
        {
            length = len;
            width = wid;
        }
    }

#780 – The Case for Immutable structs

You can run into problems when a struct is mutable.  (E.g. when used as a property, in a collection, or when modifying a struct through a method).

You can avoid problems by being careful about how you use the struct and by being aware of value type semantics (you get a copy of the value-typed object, rather than a reference to it).

You can also avoid problems by making your custom structs immutable.  This means:

  • Exposing the data in the struct exclusively through read-only properties
  • Defining methods that modify the value in the struct to return a new instance of the struct

For an example of this, look at the System.DateTime type, which is a struct.  Its properties all have only a get accessor, so you can’t change them.  And methods that change the value of a DateTime, e.g. AddDays, return a new instance of a DateTime.

#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

#778 – A struct Isn’t Mutable When Used in a Collection

struct is normally mutable, i.e. you can modify the values of its members directly.

However, if a struct is used in a collection class, like a List<T>, you can’t modify its members.  Referencing the item by indexing into the collection returns a copy of the struct, which you can’t modify.  To change an item in the list, you need to create a new instance of the struct.

            List<DogCollarInfo> collarList = new List<DogCollarInfo>();

            // Create a few instances of struct and add to list
            collarList.Add(new DogCollarInfo(0.5, 14.0));
            collarList.Add(new DogCollarInfo(0.3, 12.0));

            // Compile-time error: Can't modify '...' because it's not a variable
            collarList[1].Length = 22.0;

            // Do this instead
            collarList[1] = new DogCollarInfo(collarList[1].Width, 22.0);

If you store the structs in an array, then you can change the value of one of the struct’s members.

            DogCollarInfo[] arr = new DogCollarInfo[2];
            arr[0] = new DogCollarInfo(0.5, 14.0);
            arr[0].Length = 5.0;  // OK

#777 – A struct Isn’t Mutable When Used as a Property

A struct is normally mutable, i.e. you can modify the values of its members directly.  Assume that we have a DogCollarInfo struct with Width and Length members.  We can create the struct and then later modify it.

            // Create struct
            DogCollarInfo collar1 = new DogCollarInfo(0.5, 14.0);
            collar1.Dump();

            // Modify data in struct
            collar1.Length = 20.0;
            collar1.Dump();

777-001

However, if a struct is used as a property in another object, we can’t modify its members.

            // Create Dog object and set Collar property
            Dog d = new Dog("Kirby");
            d.Collar = collar1;

            // Compile-time error: Can't modify Collar because it's not a variable
            d.Collar.Length = 10.0;

Because the struct is a value type, the property accessor (get) returns a copy of the struct.  It wouldn’t make sense to change the copy, so the compiler warns us.

You can instead create a new copy of the struct:

            // Do this instead
            d.Collar = new DogCollarInfo(d.Collar.Width, 10.0);
            Console.WriteLine("Dog's collar:");
            d.Collar.Dump();

777-002