This article gives a brief introduction, features of constructors their usage and also an idea of how to use C# constructors.
Why Constructors?
Initialization of values of different variables is critical in any application development. C# does initialize simple data-types to default values as per the language specification in the .Net framework, but for classes that are user specific, it is obvious that you would be the only one who can decide what is an ideal default value. The means of achieving this is Constructors. There is also a perception that constructors can only be used beyond initialization, which is true to some extent. We will examine them in the following sections.
Features of a Constructor
A Method that does not have a return type. E.g.
public class in4DataGrid
{
//Constructor declaration. Note after the modifier "public" keyword, there is no
//mention of a datatype or void. Also the name is same as that of class.
public in4DataGrid()
{
// Some Code Here.
}
}
Can either be static or Instance based. E.g.
public class in4DataGrid
{
//Static Constructor. Modifiers are irrelevant here.
static in4DataGrid()
{
// Some Code Here.
}
//Instance based
public in4DataGrid()
{
// Some Code Here.
}
}
Take parameters and behave just like any other method, which means that any instructions can be encapsulated in a constructor. E.g.
public class in4DataGrid
{
private int intWidth;
private int intHeight;
//Parameters intWidth and intHeight of type int.
public in4DataGrid(int Width, int Height)
{
intWidth = Width;
intHeight = Height;
//Some Code Here.
}
}
Constructors can be declared "private or protected" even though declaring them public is a common scenario. When declared as private, one cannot create an instance of the class or derive the class.
Can be overloaded. Further, one overloaded constructor can call the other one to take advantage of encapsulation and take negate the risks of code duplication within overloaded constructors (See the following section examples).
The following section deals with the how best to leverage the features of Constructors in our classes.
How to Use?
- Use application logic in constructors carefully. Try to minimize unnecessary database operations in constructors like loading a Dataset that is used only in specific sections or on demand from the calling class. This is a performance overhead.
- Do not write redundant code in overloaded constructors. This will become a huge maintenance issue as your class hierarchy grows and you start implementing more overloaded constructors in your derived classes. The following example shows a common section where in validation of initialization logic happens. E.g.
public class in4DataGrid
{
private int intWidth;
private int intHeight;
//Overloaded constructor 1 with no parameters. But the constructor initializes //the values by calling the other constructor which holds common business //validations by using the keyword "this".
public in4DataGrid():this(200,300)
{
//Some Code Here.
}
//Overloaded constructor 2 with parameters intWidth and intHeight of type //int.
public in4DataGrid(int Width, int Height)
{
intWidth = Width;
intHeight = Height;
// Do some business validation here.
if(intWidth > intHeight)
{
//Some code here
}
}
}
- Try to use static constructors and variables when you are dealing with Global variables. This is especially a good design practice since you can initialize the static variables easily within static constructors instead of using constant variables. Other advantage would be to encapsulate your instruction logic for these variables. E.g.
public class in4DataGrid
{
private int intWidth;
private int intHeight;
private static int intMinimumHeight;
//Overloaded static constructor 1 with no parameters. But the constructor initializes the values by calling the other constructor.
static in4DataGrid()
{
//Some Business logic Here.
//Initialize the value based on logic.
intMinimumHeight=100;
}
//Overloaded instance based constructor 2 with parameters intWidth and //intHeight of type int.
public in4DataGrid(int Width, int Height)
{
if(Width > intMinimumWidth)
{
intWidth = Width;
intHeight = Height;
}
else
{
//Some Code Here.
}
}
}
- Overloaded static constructors are dangerous when used to call other constructors, so try not to call other static constructor from within.
- Design your derived class constructors carefully. This is because when an instance of derived class is created there is more than one constructor that will be called, i.e. both the base class and the derived class in the hierarchy. The constructors of the derived class first will run constructors of base class. The issue arises when you have overloaded constructor definitions in the base and derived classes. Overlooking here would prove costly. Make sure you use the keyword "base()" to make calls to overloaded base classes. E.g.
public class in4DataGrid: System.Data.DataGrid
{
//Constructor declaration. Use of keyword "base()" makes sure that the call is made to the base class(here it is System.Data.DataGrid). One can call any overloaded constructor of the base class this way.
public in4DataGrid(): base()
{
// Some Code Here.
}
}
Also note that the constructor initializers "base" and "this" are the only keywords that can be used when making a call to the base constructor, also any one among these can be specified.
Conclusion
Proper design of constructors goes a long way in solving some core problems faced in your class design. C# supports enormous flexibility to accommodate the use of various types of constructors. One has to judge the need of the application and never overlook the importance of constructors.