#694 – Sequence of Events for Chained Constructors

When you chain constructors together using the this keyword, one constructor calls another while an object is being created.

When one constructor chains to another, the constructor being chained to is executed first, followed by the body of the original constructor.

For example, assume that we have three Dog constructors, as shown below.

        public Dog(string name, int age, string favToy)
        {
            Console.WriteLine("name/age/favToy constructor executing");
        }

        public Dog(string name, int age)
            : this(name, age, "ball")
        {
            Console.WriteLine("name/age constructor executing");
        }

        public Dog(string name)
            : this(name, 1)
        {
            Console.WriteLine("name constructor executing");
        }

If we create a Dog object and pass it only a single argument, of type string, the code in the body of each constructor will be executed in the following order:

  • name/age/favToy constructor
  • name/age constructor
  • name constructor

 

#693 – Named Arguments in Constructors Allow the Most Flexibility

If you declare multiple constructors for a class, you can create constructors that support any combination of parameters.

For example:

        public Dog(string name, int age, string favToy)
        {
            // init stuff
        }

        public Dog(string name) : this(name, 1, null) { }
        public Dog(string name, string favToy) : this(name, 1, favToy) { }

Using optional parameters, you can declare a single constructor:

        public Dog(string name, int age = 1, string favToy = null)
        {
            // init stuff
        }

But when you do pass in arguments for any optional parameters, you must pass them in the correct order, without skipping any.

To get around this problem, you can use named arguments when invoking the constructor.

            // Ok to omit age, but then favToy must be named argument
            Dog spot = new Dog("Spot", favToy: "Ball");

#692 – Two Approaches for Optional Parameters in Constructors

When you create an instance of an object, you may want to pass different combinations of parameters for purposes of initializing the object.  In authoring a class, there are a couple different ways to support this.

You can declare multiple constructors, each supporting a different set of parameters.  You can use constructor chaining to invoke the main constructor, to centralize object initialization.

        public Dog(string name, int age, string favToy )
        {
            // Do all parameter validation here
            Name = name;
            Age = age;
            FavoriteToy = favToy;
        }

        public Dog(string name) : this(name, 1, null)
        {
        }

        public Dog(string name, int age) : this(name, age, null)
        {
        }

        public Dog(string name, string favToy) : this(name, 1, favToy)
        {
        }

You can also use optional parameters in a single constructor, to do the same thing.

        public Dog(string name, int age = 1, string favToy = null)
        {
            // Do all parameter validation here
            Name = name;
            Age = age;
            FavoriteToy = favToy;
        }

#670 – Static vs. Instance Initialization

Static fields and properties are data members associated with a class itself, rather with instances of that class.  A static constructor can be used to initialize static members.  Static fields can also be initialized when they are declared.

The first time that you reference a static member, the following occurs:

  • All static fields are initialized
  • The static constructor is called
  • Your code, which accesses the static member, executes

If you reference an instance method, or create an instance of the class, before referencing any static members, the following occurs:

  • All static fields are initialized
  • The static constructor is called
  • All instance fields are initialized
  • The instance constructor is called
  • Your code, which accesses an instance member, executes

For example, initializing an instance of a Dog leads to the output shown below.

Dog d = new Dog();

#645 – You Can Chain to the Default Constructor for a struct

You can use the this keyword in a constructor within a struct to invoke other constructors (constructor chaining).

public BoxSize(double x, double y)
    : this(x, y, 1.0)
    {
    }

Every struct also has a default parameterless constructor that you can explicitly invoke when creating an object of the struct’s type.

            BoxSize bs = new BoxSize();

Additionally, you can use the this keyword to chain to this default constructor from another constructor.  Doing so results in all fields being assigned to default values.

        // Initialize x and y to specified values,
        //   initialize z to default value.
        public BoxSize(double x, double y)
            : this()
        {
            this.x = x;
            this.y = y;
        }

#644 – Chaining struct Constructors

In the same way that one constructor for a class can call another, a constructor for a struct can call another constructor, using the this keyword.  In this way, one constructor for the struct calls another, to help initialize the data in the struct.

In the example below, we define two constructors for the BoxSize struct.  The constructor that takes only two parameters chains to the three-parameter constructor, using the this keyword.

    public struct BoxSize
    {
        public double x;
        public double y;
        public double z;

        // Constructor that fully initializes the object
        public BoxSize(double x, double y, double z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        // 2nd constructor that allows user to specify
        //   only X & Y and we then default Z to 1.0
        public BoxSize(double x, double y)
            : this(x, y, 1.0)
        {
        }

        public void PrintBoxSize()
        {
            Console.WriteLine(string.Format("X={0}, Y={1}, Z={2}", x, y, z));
        }
    }

#590 – Optional Parameters in Constructors

You’ll typically see optional parameters in methods.  But you can also define an optional parameter in a constructor:

        public string Name { get; set; }
        public int Age { get; set; }
        public string FavoriteToy { get; set; }
        public Dog Father { get; set; }
        public Dog Mother { get; set; }

        public Dog(string name, int age = 1, string favToy = "Bone",
            Dog father = null, Dog mother = null)
        {
            Name = name;
            Age = age;
            FavoriteToy = favToy;
            Father = father;
            Mother = mother;
        }

 

            Dog kirby = new Dog("Kirby", 15, "Ball");
            Dog sonOfKirby = new Dog("Ferbie", 2, "Frisbee", kirby);


Optional parameters in constructors avoid the need to chain constructors.

 

#298 – A struct Can Have Several Constructors

You can define more than one constructor for a struct, as long as the method signature for each constructor is different.

    public struct Point3D
    {
        public double x;
        public double y;
        public double z;

        public Point3D(double xi, double yi, double zi)
        {
            x = xi;
            y = yi;
            z = zi;
        }

        public Point3D(int n)
        {
            x = n;
            y = n;
            z = n;
        }
    }

Now you can instantiate a variable of this type in several different ways.

            Point3D pt1 = new Point3D();    // Default parameterless constructor
            Point3D pt2 = new Point3D(10.0, 20.0, 30.0);
            Point3D pt3 = new Point3D(5);

#295 – When Is a Static Constructor Called?

You cannot dictate when a static constructor will be called and you can’t call it directly.

More specifically, a static constructor will be called just before you do one of the following:

  • Create an instance of the type
  • Access any static data in the type
  • Start executing Main method (in the same class as the static constructor)

If we have the following implementation in the Dog class:

        public Dog(string name)
        {
            Console.WriteLine("Dog constructor");
            Name = name;
        }

        public static string Motto = "Wag all the time";

        static Dog()
        {
            Console.WriteLine("Static Dog constructor");
        }

And we create a Dog instance:

 Console.WriteLine("Here 1");
 Dog d1 = new Dog("Kirby");
 Console.WriteLine("Here 2");

We get this output (static constructor called before object instantiation):

Or if we access static data:

            Console.WriteLine("Here 1");
            string s = Dog.Motto;
            Console.WriteLine("Here 2");

The output is (static constructor called before accessing static data):

#294 – Make All Constructors Private to Prevent Object Creation

If you want to prevent code external to a class from creating instances of that class, you can make all of the constructors of the class private.

In the following example, we have a single Dog constructor, which is private.

        private Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

Because the constructor is private, code outside the Dog class cannot create a new instance of a Dog.

But we can have a static method in the Dog class that can create instances.  Because the code is defined inside the Dog class, it has access to the private constructor.

public class Dog
{
        // code omitted

        public static Dog MakeADog()
        {
            // Use private constructor
            Dog nextDog = new Dog(nameList[nextDogIndex], ageList[nextDogIndex]);

            nextDogIndex = (nextDogIndex == (nameList.Length - 1)) ? 0 : nextDogIndex++;

            return nextDog;
        }

Now if we want a new Dog instance, we can call the MakeADog method.