The Constructor as a Method

A Constructor with Parameters

In your class, you can create a constructor with as many parameters as you want. Here is an example of a constructor that uses three parameters:

public class Exercise
{
    public Exercise(string a, int b, int c)
    {
    }
}

In the body of the constructor, you can ignore or use the values of the parameters. To create an object of the class, declare a variable of the class and initialize it using the constructor. This means that you must pass an argument for each parameter.

ApplicationPractical Learning: Introducing Object Construction

  1. Start Microsoft Visual Studio and create a new Console App (that support the .NET 7.0 (Standard Term Support) named Chemistry2
  2. To create a new folder, in the Solution Explorer, right-click Chemistry2 -> Add -> New Folder
  3. Type Models as the name of the folder
  4. In the Solution Explorer, right-click Models -> Add -> Class...
  5. Change the file Name to Element
  6. Press Enter
  7. Based on what we learned in the previous lesson, change the Element class as follows:
    namespace Chemistry2.Models
    {
        public class Element
        {
            internal string Symbol       { get; set; }
            internal string ElementName  { get; set; }
            internal int    AtomicNumber { get; set; }
            internal double AtomicWeight { get; set; }
    
            public Element()
            {
                Symbol = "H";
                ElementName = "Hydrogen";
                AtomicNumber = 1;
                AtomicWeight = 1.008;
    	}
        }
    }
  8. In the Solution Explorer, right-click Program.cs -> Rename
  9. Type PeriodicTable (to get PeriodicTable.cs) and press Enter
  10. Read the content of the message box and click Yes
  11. Click the PeriodicTable.cs tab and change the document as follows:
    using Chemistry2.Models;
    using static System.Console;
    
    Element h = new() { ElementName = "Hydrogen", AtomicWeight = 1.008, AtomicNumber = 1, Symbol = "H" };
    Element he = new() { ElementName = "Helium", AtomicWeight = 4.002602, AtomicNumber = 2, Symbol = "He" };
    Element li = new() { ElementName = "Lithium", AtomicWeight = 6.94, AtomicNumber = 3, Symbol = "Li" };
    Element be = new();
    
    be.Symbol = "Be";
    be.AtomicNumber = 4;
    be.ElementName = "Beryllium";
    be.AtomicWeight = 9.0121831;
    
    Element b = new() { ElementName = "Boron", AtomicWeight = 10.81, AtomicNumber = 5, Symbol = "B" };
    Element c = new() { Symbol = "C", AtomicNumber = 6, ElementName = "Carbon", AtomicWeight = 12.011 };
    Element n = new() { AtomicNumber = 7, Symbol = "N", ElementName = "Nitrogen", AtomicWeight = 14.007 };
    
    WriteLine("Chemistry");
    WriteLine("------------------------");
    WriteLine("Symbol:        " + n.Symbol);
    WriteLine($"Atomic Number: {n.AtomicNumber}");
    WriteLine("Element Name:  " + n.ElementName);
    WriteLine($"Atomic Weight: " + n.AtomicWeight);
    Write("========================");
  12. To execute the project, press Ctrl + F5:
    Chemistry
    ------------------------
    Symbol:        N
    Atomic Number: 7
    Element Name:  Nitrogen
    Atomic Weight: 14.007
    ========================
    
    Press any key to close this window . . .
  13. To close the window and return to your programming environment, press U
  14. To create and use a Constructor with many parameters, click the Element.cs tab to access the class and change it as follows:
    namespace Chemistry2.Models
    {
        public class Element
        {
            internal string Symbol       { get; set; }
            internal string ElementName  { get; set; }
            internal int    AtomicNumber { get; set; }
            internal double AtomicWeight { get; set; }
    
            public Element(int number, string symbol, string name, double mass)
            {
                Symbol = symbol;
                ElementName = name;
                AtomicWeight = mass;
                AtomicNumber = number;
    	}
        }
    }
  15. Click the Chemistry.cs tab and change the code as follows:
    using Chemistry2.Models;
    using static System.Console;
    
    Element h = new Element(1, "H", "Hydrogen", 1.008);
    Element he = new Element(2, "He", "Helium", 4.002602);
    Element li = new Element(3, "Li", "Lithium", 6.94);
    Element be = new(4, "Be", "Beryllium", 9.0121831);
    Element b = new(5, "B", "Boron", 10.81);
    Element n = new(7, "N", "Nitrogen", 14.007);
    
    Element o = new(8, "O", "Oxygen", 15.999);
    
    WriteLine("Chemistry");
    WriteLine("------------------------");
    WriteLine("Symbol:        " + n.Symbol);
    WriteLine($"Atomic Number: {n.AtomicNumber}");
    WriteLine("Element Name:  " + n.ElementName);
    WriteLine($"Atomic Weight: " + n.AtomicWeight);
    Write("========================");
  16. To execute the application and see the result, on the main menu, click Debug -> Start Without Debugging:
    Chemistry
    ------------------------
    Symbol:        O
    Atomic Number: 8
    Element Name:  Oxygen
    Atomic Weight: 15.999
    ========================
    
    Press any key to close this window . . .
  17. To close the window and return to your programming environment, press J

Passing an Argument by Name

As seen with methods, if you call a constructor that takes at least one argument, you can access the/each parameter by its name, followed by a colon, and its value. Here is an example:

using static System.Console;

Element c = new(name: "Carbon", mass: 12.011, symbol: "C", number: 6);

Constructor Overloading

In your class, you can create as many constructors as you want. If you decide to create different constructors, they must follow the rules of method overloading. This means that each constructor must be different from the others. To take care of this, each constructor can use a different number of parameters or different data types for parameters compared to other constructors. Here are examples:

public class Exercise
{
    public Exercise(string a)
    {
    }

    public Exercise(string a, int b, int c)
    {
    }
}

If you create different constructors, when creating (an) instance(s) of the class (when declaring a (the) variable(s) of the class), you can choose what constructor is appropriate to initialize the variable.

Consider the following class and its constructor:

public class Element
{
    string _name_;
    string _symbol_;
    int    _number_;
    double _weight_;

    public Element(int number, string symbol, string name, double mass)
    {
        _name_   = name;
        _weight_ = mass;
        _symbol_ = symbol;
        _number_ = number;
    }
}

If you create a class with only one constructor, when declaring an instance of the class, you must use that constructor: you cannot use the default constructor that doesn't use any parameter. When declaring the variable, initialize it with a constructor with parentheses and provide the value(s) in the parentheses of the constructor.

ApplicationPractical Learning: Overloading a Constructor

  1. Click the Element.cs tab and change its class as follows:
    namespace Chemistry2.Models
    {
        public class Element
        {
            internal string Symbol       { get; set; }
            internal string ElementName  { get; set; }
            internal int    AtomicNumber { get; set; }
            internal double AtomicWeight { get; set; }
    
            public Element(int number)
            {
                AtomicNumber = number;
            }
    
            public Element(string symbol)
            {
                Symbol = symbol;
            }
    
            public Element(int number, string symbol, string name, double mass)
            {
                Symbol = symbol;
                ElementName = name;
                AtomicWeight = mass;
                AtomicNumber = number;
    	}
        }
    }
  2. Click the Chemistry.cs tab and change the code of the Click event as follows:
    using Chemistry2.Models;
    using static System.Console;
    
    Element h = new Element(1, "H", "Hydrogen", 1.008);
    Element he = new Element(2, "He", "Helium", 4.002602);
    Element li = new Element(3, "Li", "Lithium", 6.94);
    Element be = new(4, "Be", "Beryllium", 9.0121831);
    Element b = new(5, "B", "Boron", 10.81);
    Element c = new(name: "Carbon", mass: 12.011, symbol: "C", number: 6);
    Element n = new(7, "N", "Nitrogen", 14.007);
    Element o = new(8, "O", "Oxygen", 15.999);
    
    Element f = new Element(9);
    f.Symbol = "F";
    f.AtomicWeight = 18.998;
    f.ElementName = "Fluorine";
    
    Element ne = new Element("Ne");
    ne.AtomicNumber = 10;
    ne.AtomicWeight = 20.180;
    ne.ElementName = "Neon";
    
    WriteLine("Chemistry");
    WriteLine("------------------------");
    WriteLine("Symbol:        " + f.Symbol);
    WriteLine($"Atomic Number: {f.AtomicNumber}");
    WriteLine("Element Name:  " + f.ElementName);
    WriteLine($"Atomic Weight: " + f.AtomicWeight);
    WriteLine("========================");
    
    WriteLine("Symbol:        " + ne.Symbol);
    WriteLine($"Atomic Number: {ne.AtomicNumber}");
    WriteLine("Element Name:  " + ne.ElementName);
    WriteLine($"Atomic Weight: " + ne.AtomicWeight);
    Write("========================");
  3. To execute the application, on the main menu, click Debug -> Start Without Debugging:
    Chemistry
    ------------------------
    Symbol:        F
    Atomic Number: 9
    Element Name:  Fluorine
    Atomic Weight: 18.998
    ========================
    Symbol:        Ne
    Atomic Number: 10
    Element Name:  Neon
    Atomic Weight: 20.18
    ========================
    
    Press any key to close this window . . .
  4. To close the window and return to your programming environment, press M

A Boolean Parameter in a Constructor

A constructor can use a Boolean parameter. Here is an example:

public class Contractor
{
    public Contractor(bool validated)
    {
    }

    void CalculatePayroll(bool fullTime)
    {
    }

    void ValidateEmploymentStatus()
    {
        CalculatePayroll(true);
    }
}

public class Accountability
{
    public Accountability()
    {
        Contractor empl = new Contractor(true);

        var staff = new Contractor(false);
    }
}

The Absence of a Default Constructor

If you create a class with only one constructor and that constructor uses at least one parameter, the default constructor would not be available anymore. If you want to access a default constructor of an object, you have two options:

A class is usually made to contain many members. The primary reason you create a constructor is to have a tool to initialize an object of the class with one or some default values. The primary reason you create different constructors is to provide different values to objects depending on what constructor a user (actually a programmer) wants to use to create an object. To make this happen, you can initialize the members with values passed to the parameter(s). Here are examples:

using static System.Console;

WriteLine("Using Constructors");
WriteLine("==================================");
Exercise exo = new Exercise();
exo = new Exercise("C# Programming");
exo = new Exercise("A wondewrful world", 248);
exo = new Exercise("A programming endevour", 557, 3972);

public class Exercise
{
    string text;
    int    wdt, hgt;

    public Exercise()
    {
        text = "Empty";

        WriteLine("Default constructor");
        WriteLine("----------------------------------");
        WriteLine($"Contents:    {text}");
        WriteLine($"Dimension 1: {wdt}");
        WriteLine($"Dimension 2: {hgt}");
        WriteLine("==================================");
    }

    public Exercise(string title)
    {
        text = title;

        WriteLine("A constructor with one argument");
        WriteLine("----------------------------------");
        WriteLine($"Contents:    {text}");
        WriteLine($"Dimension 1: {wdt}");
        WriteLine($"Dimension 2: {hgt}");
        WriteLine("==================================");
    }

    public Exercise(string title, int width)
    {
        wdt = width;
        text = title;

        WriteLine("A constructor with two arguments");
        WriteLine("----------------------------------");
        WriteLine($"Contents:    {text}");
        WriteLine($"Dimension 1: {wdt}");
        WriteLine($"Dimension 2: {hgt}");
        WriteLine("==================================");
    }

    public Exercise(string title, int width, int height)
    {
        text = title;
        wdt  = width;
        hgt  = height;

        WriteLine("A constructor with three arguments");
        WriteLine("----------------------------------");
        WriteLine($"Contents:    {text}");
        WriteLine($"Dimension 1: {wdt}");
        WriteLine($"Dimension 2: {hgt}");
        WriteLine("==================================");
    }
}

A Constructor with Default Values

Since a constructor is primarily a method, its parameter(s), if any, can use default values. The rules are exactly the same we reviewed for methods. To provide a default value for the parameter of a constructor, assign the desired but appropriate value to the parameter when creating the constructor. Here is an example:

public class Exercise
{
    public Exercise(string caption = "Exercise")
    {
    }
}

Once again, in the body of the constructor, you can use or ignore the parameter. If you create one constructor and it uses one parameter, when creating an object of the class, that single constructor would act as both (or either) a default constructor and (or) a constructor that uses one parameter. This means that you can declare a variable and use a constructor with empty parentheses. Here is an example:

using static System.Console;

public class Employee
{
    public Employee(string name = "John Doe")
    {
        WriteLine("==================================");
        WriteLine($"Employee Name: {name}");
        WriteLine("==================================");
    }
}

Employee staff = new Employee();

In the same way, you can create a constructor that uses different parameters and some parameters can have default values. When doing this, make sure you follow the rules we reviewed for functions (or methods) that have default values for parameters. Here is an example:

using static System.Console;

public class Employee
{
    public Employee(int nbr = 111_111, string fname = "John",
                    string lname = "Doe", double hsal = 0.00)
    {
        WriteLine("==================================");
        WriteLine($"Employee #:    {nbr}");
        WriteLine($"Employee Name: {fname} {lname}");
        WriteLine($"Hourly Salary: {hsal}");
    }
}

// Creating an object without arguments
Employee staff = new Employee();
        
// Passing one argument
staff = new Employee(283_583);
// Passing three arguments
staff = new Employee(928_495, "James", "Dickson");
// Passing all arguments
staff = new Employee(492_859, "Elisabeth", "Hoppers", 22.28);
// Passing arguments by names
staff = new Employee(lname: "Turner", nbr: 592_804, hsal: 17.85, fname: "Chritian");
WriteLine("==================================");

This would produce:

==================================
Employee #:    111111
Employee Name: John Doe
Hourly Salary: 0
==================================
Employee #:    283583
Employee Name: John Doe
Hourly Salary: 0
==================================
Employee #:    928495
Employee Name: James Dickson
Hourly Salary: 0
==================================
Employee #:    492859
Employee Name: Elisabeth Hoppers
Hourly Salary: 22.28
==================================
Employee #:    592804
Employee Name: Chritian Turner
Hourly Salary: 17.85
==================================

Press any key to close this window . . .

Initializing a Narrower Constructor

We have seen that you can create many constructors in the same class, which is referred to as overloading a constructor. Of course, the constructors must vary by their syntaxes. To start, you can overload a constructor with at least two versions. Here is an example:

namespace QuatroGas.Models
{
    internal class GasMeter
    {
        public string MeterNumber { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }

        public GasMeter()
        { 
        }

        public GasMeter(string number)
        {
            MeterNumber = number;
        }
    }
}

If you create more than one constructor in a class, you can initialize the fields or properties of the class in a constructor that has fewer parameters by using the syntax of a constructor that has more parameters. To do this, you use an object named this. This object is used as if you are calling a constructor that uses the same number and types of parameters as a constructor in your class. To use it, just after the closing parenthesis of the constructor where you are using the this object, type this precede it with a colon and add some parentheses to this object. In the parentheses, provide a value for each of the parameters of the constructor you are calling. Here is an example:

namespace QuatroGas2.Models
{
    internal class GasMeter
    {
        public string MeterNumber { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }

        /* This is the default constructor.
         * Use it to initialize the property 
         * that is passed to the other constructor. */
        public GasMeter()
            : this("000-000")
        { 
        }

        public GasMeter(string number)
        {
            MeterNumber = number;
        }
    }
}

After doing this, you can access the constructor that was initialized and use its values. Here is an example:

GasMeter gasMeter = new GasMeter();

Console.WriteLine("Quatro Gas");
Console.WriteLine("============================================");
Console.WriteLine("Gas Meter Details");
Console.WriteLine("--------------------------------------------");
Console.WriteLine("Meter #: {0}", gasMeter.MeterNumber);
Console.WriteLine("Make:    {0}", gasMeter.Make);
Console.WriteLine("Model:   {0}", gasMeter.Model);
Console.WriteLine("============================================");

internal class GasMeter
{
    public string MeterNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }

    public GasMeter()
        : this("888-888")
    {
    }

    public GasMeter(string number)
    {
        MeterNumber = number;
    }
}

This would produce:

Quatro Gas
============================================
Gas Meter Details
--------------------------------------------
Meter #: 888-888
Make:
Model:
============================================

In the same way, if you have many constructors, you can use the syntaxes of some to initialize the properties in others. Here is an example:

namespace QuatroGas.Models
{
    internal class Customer
    {
        public string AccountNumber { get; set; }
        public GasMeter GasMeter    { get; set; }
        public string AccountName   { get; set; }
        public string Address       { get; set; }
        public string City          { get; set; }
        public string County        { get; set; }
        public string State         { get; set; }

        // This is a constructor with two parameters.
        public Customer(string number, string name)
        {
            AccountNumber = number;
            AccountName = name;
        }

        // This constructor applies the constructor that has three parameters.
        public Customer()
            : this("0000-000-0000", "john Doe", "NY")
        {

        }

        // This constructor applies the constructo that uses two parameters.
        public Customer(string number)
            : this("Jane", "Unknown")
        {
            AccountNumber = number;
        }

        // This constructor uses the constructor with one parameter
        public Customer(string number, string name, string residence)
            : this("TX")
        {
            AccountNumber = number;
            AccountName = name;
            State = residence;
        }
    }
}

With this technique, when creating a constructor, use the this object to access another constructor and initialize the desired properties; then initialize the remaining property (or properties) in the body of the constructor you are accessing. Here are examples:

namespace QuatroGas.Models
{
    internal class GasMeter
    {
        public string MeterNumber { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }

        /* This is the default constructor.
         * Initialize it with the three-parameter constructor.*/
        public GasMeter()
            : this("888-888", "Generic", "AAA")
        { 
        }

        /* This is a one-parameter constructor.
         * Initialize it with the three-parameter constructor 
         * but use this constructor's one-parameter for the 
         * first parameter of the three-parameter constructor. */
        public GasMeter(string number)
            : this(number, "Generic", "AAA")
        {
        }

        public GasMeter(string number, string make, string model)
        {
            MeterNumber = number;
            Make = make;
            Model = model;
        }
    }
}

After creating the constructors like that, when creating an object, you can call the constructor you want. The property whose value you had provided would be applied. The missing property (properties) would use the value(s) that was (were) provided in the class. Here is one example of creating an object using the default constructor of the above class (which doesn't provide any value for the properties)

using QuatroGas.Models;

/* Provide no value for the Gas Meter object.
 * The values will come from the default constructor. */
GasMeter gasMeter = new GasMeter();

Console.WriteLine("Quatro Gas");
Console.WriteLine("============================================");
Console.WriteLine("Gas Meter Details");
Console.WriteLine("--------------------------------------------");
Console.WriteLine("Meter #: {0}", gasMeter.MeterNumber);
Console.WriteLine("Make:    {0}", gasMeter.Make);
Console.WriteLine("Model:   {0}", gasMeter.Model);
Console.WriteLine("============================================");

This would produce:

Quatro Gas
============================================
Gas Meter Details
--------------------------------------------
Meter #: 888-888
Make:    Generic
Model:   AAA
============================================

Press any key to close this window . . .

Here is one example of creating an object using the default constructor of the above class (which doesn't provide any value for the properties)

using QuatroGas.Models;

/* Provide only the Gas Meter #.
 * The Make and Model will come from the constructor */
GasMeter gasMeter = new GasMeter("180-700");

Console.WriteLine("Quatro Gas");
Console.WriteLine("============================================");
Console.WriteLine("Gas Meter Details");
Console.WriteLine("--------------------------------------------");
Console.WriteLine("Meter #: {0}", gasMeter.MeterNumber);
Console.WriteLine("Make:    {0}", gasMeter.Make);
Console.WriteLine("Model:   {0}", gasMeter.Model);
Console.WriteLine("============================================");

This would produce:

Quatro Gas
============================================
Gas Meter Details
--------------------------------------------
Meter #: 180-700
Make:    Generic
Model:   AAA
============================================

Press any key to close this window . . .

When using the this object, in its parentheses, to make your code easy to read, you can access a parameter by its name and assign the desired value to it. For each parameter, you must the name of the parameter of the constructor you are using. Consider the following examples:

namespace QuatroGas.Models
{
    internal class Customer
    {
        public string AccountNumber { get; set; }
        public string AccountName   { get; set; }
        public string Address       { get; set; }
        public string City          { get; set; }
        public string County        { get; set; }
        public string State         { get; set; }

        // This is a constructor with two parameters.
        public Customer(string actNbr, string identifier)
        {
            AccountNumber = actNbr;
            AccountName = identifier;
        }

        // This constructor applies the constructor that has three parameters.
        public Customer()
            : this(number: "0000-000-0000", name: "john Doe", residence: "NY")
        {

        }

        // This constructor applies the constructo that uses two parameters.
        public Customer(string nbr)
            : this(actNbr: "Jane", identifier: "Unknown")
        {
            AccountNumber = nbr;
        }

        // This constructor uses the constructor with one parameter
        public Customer(string number, string name, string residence)
            : this(nbr: "TX")
        {
            AccountNumber = number;
            AccountName = name;
            State = residence;
        }
    }
}

¶emember that a parameter of a constructor can be an object. You can initialize such a parameter in a this object. Here are two examples:

namespace QuatroGas2.Models
{
    internal class Customer
    {
        public string AccountNumber { get; set; }
        public GasMeter GasMeter    { get; set; }
        public string AccountName   { get; set; }
        public string Address       { get; set; }
        public string City          { get; set; }
        public string County        { get; set; }
        public string State         { get; set; }

        // This is a constructor with two parameters.
        public Customer(string actNbr, string identifier)
        {
            AccountNumber = actNbr;
            AccountName = identifier;
        }

        // This constructor applies the constructor that has three parameters.
        public Customer()
            : this(number: "0000-000-0000", name: "john Doe", residence: "NY")
        {

        }

        // This constructor applies the constructo that uses two parameters.
        public Customer(string nbr)
            : this(actNbr: "Jane", identifier: "Unknown")
        {
            AccountNumber = nbr;
        }

        // This constructor uses the constructor with one parameter
        public Customer(string number, string name, string residence)
            : this(nbr: "TX")
        {
            AccountNumber = number;
            AccountName = name;
            State = residence;
        }

        // This constructor applies the constructo that uses two parameters.
        public Customer(GasMeter gMtr)
            : this("1111-222-3333",
                   new GasMeter("222-444", "Generix", "abc"))
        {
        }

        // This is a constructor with two parameters.
        public Customer(string actNbr, GasMeter gm, string identifier)
        {
            AccountNumber = actNbr;
            AccountName = identifier;
            GasMeter = gm;
        }

        // This constructor applies the constructo that uses two parameters.
        public Customer(string nbr, GasMeter gm)
            : this(actNbr: "Jane",
                   gm: new GasMeter("555-55", "Supra-Side", "ABC"),
                   identifier: "John Dryer")
        {
            AccountNumber = nbr;
        }
    }
}

The Destruction of an Object

The Destructor of a Class

A destructor is a special method of a class. While a constructor is called when an object is created, a destructor is called when an object is not used anymore. The job of a destructor is to do the cleaning of the computer memory that an object was occupying when the object was used. Like the default constructor, the compiler always creates a default destructor if you don't create one. Unlike the constructor, the destructor cannot be overloaded. This means that, if you decide to create a destructor, you can have only one. Like the default constructor, a destructor also has the same name as its class. This time, the name of the destructor starts with a tilde "~".

To create a destructor, type ~ followed by the name of the class. Here is an example:

public class Exercise
{
    public Exercise(string caption = "Exercise", int width = 640, int height = 480)
    {
    }

    ~Exercise()
    {
    }
}

Garbage Collection

When you initialize a variable using the new operator, you are in fact reserving some space in the section of memory called the heap. Such memory is "allocated" for the variable. When that variable is no longer needed, such as when your program closes, the variable must be removed from memory and the space it was using should (must) be made available to other variables or other programs. In fact, when an object has been removed from memory, it is replaced by garbage, which is some value but that is of no use (that area of memory becomes filled with garbage). If you program in some languages such as C/C++, Assembly, Pascal, etc, you should (must) find a way to remove that garbage (it is not difficult, sometimes it takes a simple/single line of code, but you should (must) remember to do it); that is, you should (must) free the memory a variable (declared with new, and called a reference) was using. Freeing the memory is referred to as garbage collection. Normally, in languages like C++ or Pascal, that's one of the ways you use a destructor.

The .NET library solves the problem of garbage collection by "cleaning" the memory after you. This is done automatically when necessary so that the programmer doesn't need to worry about this issue.

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2001-2023, C# Key Saturday 29 April 2023 Next