Thursday, October 22, 2009

Understanding Class Constructors

Given that objects have state (represented by the values of an object’s member variables), the object
user will typically want to assign relevant values to the object’s field data before use. Currently, the
Car type demands that the petName and currSpeed fields be assigned on a field-by-field basis. For the
current example, this is not too problematic, given that we have only two public data points. However,
it is not uncommon for a class to have dozens of fields to contend with. Clearly, it would be
undesirable to author 20 initialization statements to set 20 points of data.
Thankfully, C# supports the use of class constructors, which allow the state of an object to be
established at the time of creation. A constructor is a special method of a class that is called indirectly
when creating an object using the new keyword. However, unlike a “normal” method,
constructors never have a return value (not even void) and are always named identically to the
class they are constructing.
nNote As shown in Chapter 13, C# 2008 provides a new object initialization syntax, which allows you to set the
values of public fields and invoke public properties at the time of construction.
The Role of the Default Constructor
Every C# class is provided with a freebee default constructor that you may redefine if need be. By
definition, a default constructor never takes arguments. Beyond allocating the new object into
memory, the default constructor ensures that all field data is set to an appropriate default value
(see Chapter 3 for information regarding the default values of C# data types).
144 CHAPTER 5 n DEFINING ENCAPSULATED CLASS TYPES
If you are not satisfied with these default assignments, you may redefine the default constructor
to suit your needs. To illustrate, update your C# Car class as follows:
class Car
{
// The 'state' of the Car.
public string petName;
public int currSpeed;
// A custom default constructor.
public Car()
{
petName = "Chuck";
currSpeed = 10;
}
...
}
In this case, we are forcing all Car objects to begin life named Chuck at a rate of 10 mph. With
this, you are able to create a Car object set to these default values as follows:
static void Main(string[] args)
{
// Invoking the default constructor.
Car chuck = new Car();
// Prints "Chuck is going 10 MPH."
chuck.PrintState();
}

Defining Custom Constructors
Typically, classes define additional constructors beyond the default. In doing so, you provide the
object user with a simple and consistent way to initialize the state of an object directly at the time
of creation. Ponder the following update to the Car class, which now supports a total of three class
constructors:
class Car
{
// The 'state' of the Car.
public string petName;
public int currSpeed;
// A custom default constructor.
public Car()
{
petName = "Chuck";
currSpeed = 10;
}
// Here, currSpeed will receive the
// default value of an int (zero).
public Car(string pn)
{
petName = pn;
}

// Let caller set the full 'state' of the Car.
public Car(string pn, int cs)
{
petName = pn;
currSpeed = cs;
}
...
}
Keep in mind that what makes one constructor different from another (in the eyes of the C#
compiler) is the number of and type of constructor arguments. Recall from Chapter 4, when you
define a method of the same name that differs by the number or type of arguments, you have
overloaded the method. Thus, the Car type has overloaded the constructor to provide a number of
ways to create the object at the time of declaration. In any case, you are now able to create Car
objects using any of the public constructors. For example:
static void Main(string[] args)
{
// Make a Car called Chuck going 10 MPH.
Car chuck = new Car();
chuck.PrintState();
// Make a Car called Mary going 0 MPH.
Car mary = new Car("Mary");
mary.PrintState();
// Make a Car called Daisy going 75 MPH.
Car daisy = new Car("Daisy", 75);
daisy.PrintState();
}

The Default Constructor Revisited
As you have just learned, all classes are endowed with a free default constructor. Thus, if you insert a
new class into your current project named Motorcycle, defined like so:
class Motorcycle
{
public void PopAWheely()
{
Console.WriteLine("Yeeeeeee Haaaaaeewww!");
}
}
you are able to create an instance of the Motorcycle type via the default constructor out of the box:
static void Main(string[] args)
{
Motorcycle mc = new Motorcycle();
mc.PopAWheely();
}
However, as soon as you define a custom constructor, the default constructor is silently removed
from the class and is no longer available! Think of it this way: if you do not define a custom constructor,
the C# compiler grants you a default in order to allow the object user to allocate an
instance of your type with field data set to the correct default values. However, when you define
a unique constructor, the compiler assumes you have taken matters into your own hands.

Therefore, if you wish to allow the object user to create an instance of your type with the
default constructor, as well as your custom constructor, you must explicitly redefine the default. To
this end, understand that in a vast majority of cases, the implementation of the default constructor
of a class is intentionally empty, as all you require is the ability to create an object with default
values. Consider the following update to the Motorcycle class:
class Motorcycle
{
public int driverIntensity;
public void PopAWheely()
{
for (int i = 0; i <= driverIntensity; i++)
{
Console.WriteLine("Yeeeeeee Haaaaaeewww!");
}
}
// Put back the default constructor.
public Motorcycle() {}
// Our custom constructor.
public Motorcycle(int intensity)
{ driverIntensity = intensity; }
}

The Role of the this Keyword
Like other C-based languages, C# supplies a this keyword that provides access to the current class
instance. One possible use of the this keyword is to resolve scope ambiguity, which can arise when
an incoming parameter is named identically to a data field of the type. Of course, ideally you would
simply adopt a naming convention that does not result in such ambiguity; however, to illustrate this
use of the this keyword, update your Motorcycle class with a new string field (named name) to represent
the driver’s name. Next, add a method named SetDriverName() implemented as follows:
class Motorcycle
{
public int driverIntensity;
public string name;
public void SetDriverName(string name)
{ name = name; }
...
}
Although this code will compile just fine, if you update Main() to call SetDriverName() and then
print out the value of the name field, you may be surprised to find that the value of the name field is an
empty string!
// Make a Motorcycle with a rider named Tiny?
Motorcycle c = new Motorcycle(5);
c.SetDriverName("Tiny");
c.PopAWheely();
Console.WriteLine("Rider name is {0}", c.name); // Prints an empty name value!

The problem is that the implementation of SetDriverName() is assigning the incoming parameter
back to itself given that the compiler assumes name is referring to the variable currently in the
method scope rather than the name field at the class scope. To inform the compiler that you wish to
set the current object’s name data field to the incoming name parameter, simply use this to resolve
the ambiguity:
public void SetDriverName(string name)
{ this.name = name; }
Do understand that if there is no ambiguity, you are not required to make use of the this keyword
when a class wishes to access its own data or members. For example, if we rename the string
data member to driverName, the use of this is optional as there is no longer a scope ambiguity:
class Motorcycle
{
public int driverIntensity;
public string driverName;
public void SetDriverName(string name)
{
// These two statements are functionally the same.
driverName = name;
this.driverName = name;
}
...
}
Even though there is little to be gained when using this in unambiguous situations, you may
still find this keyword useful when implementing members, as IDEs such as SharpDevelop and
Visual Studio 2008 will enable IntelliSense when this is specified. This can be very helpful when
you have forgotten the name of a class member and want to quickly recall the definition. Consider

Note It is a compiler error to use the this keyword within the implementation of a static member (explained
shortly). As you will see, static members operate on the class (not object) level, and therefore at the class level,
there is no current object (thus no this)!

Chaining Constructor Calls Using this
Another use of the this keyword is to design a class using a technique termed constructor chaining.
This design pattern is helpful when you have a class that defines multiple constructors. Given the
fact that constructors often validate the incoming arguments to enforce various business rules, it
can be quite common to find redundant validation logic within a class’s constructor set. Consider
the following updated Motorcycle:
class Motorcycle
{
public int driverIntensity;
public string driverName;
public Motorcycle() { }
// Redundent constructor logic!
public Motorcycle(int intensity)
{
if (intensity > 10)
{
intensity = 10;
}
driverIntensity = intensity;
}
public Motorcycle(int intensity, string name)
{
if (intensity > 10)
{
intensity = 10;
}
driverIntensity = intensity;
driverName = name;
}
...
}
Here (perhaps in an attempt to ensure the safety of the rider), each constructor is ensuring that
the intensity level is never greater than 10. While this is all well and good, we do have redundant
code statements in two constructors. This is less than ideal, as we are now required to update code
in multiple locations if our rules change (for example, if the intensity should not be greater than 5).
One way to improve the current situation is to define a method in the Motorcycle class that will
validate the incoming argument(s). If we were to do so, each constructor could make a call to this
method before making the field assignment(s). While this approach does allow us to isolate the
code we need to update when the business rules change, we are now dealing with the following
redundancy:

class Motorcycle
{
public int driverIntensity;
public string driverName;
// Constructors.
public Motorcycle() { }
public Motorcycle(int intensity)
{
SetIntensity(intensity);
}
public Motorcycle(int intensity, string name)
{
SetIntensity(intensity);
driverName = name;
}
public void SetIntensity(int intensity)
{
if (intensity > 10)
{
intensity = 10;
}
driverIntensity = intensity;
}
...
}
A cleaner approach is to designate the constructor that takes the greatest number of arguments
as the “master constructor” and have its implementation perform the required validation logic. The
remaining constructors can make use of the this keyword to forward the incoming arguments to
the master constructor and provide any additional parameters as necessary. In this way, we only
need to worry about maintaining a single constructor for the entire class, while the remaining constructors
are basically empty.
Here is the final iteration of the Motorcycle class (with one additional constructor for the sake
of illustration). When chaining constructors, note how the this keyword is “dangling” off the constructor’s
declaration (via a colon operator) outside the scope of the constructor itself:
class Motorcycle
{
public int driverIntensity;
public string driverName;
// Constructor chaining.
public Motorcycle() {}
public Motorcycle(int intensity)
: this(intensity, "") {}
public Motorcycle(string name)
: this(0, name) {}
// This is the 'master' constructor that does all the real work.
public Motorcycle(int intensity, string name)
{
if (intensity > 10)
{
intensity = 10;

}
driverIntensity = intensity;
driverName = name;
}
...
}
Understand that using the this keyword to chain constructor calls is never mandatory. However,
when you make use of this technique, you do tend to end up with a more maintainable and
concise class definition. Again, using this technique you can simplify your programming tasks, as
the real work is delegated to a single constructor (typically the constructor that has the most parameters),
while the other constructors simply “pass the buck.”

Observing Constructor Flow
On a final note, do know that once a constructor passes arguments to the designated master constructor
(and that constructor has processed the data), the constructor invoked originally by the
caller will finish executing any remaining code statements. To clarify, update each of the constructors
of the Motorcycle class with a fitting call to Console.WriteLine():
class Motorcycle
{
public int driverIntensity;
public string driverName;
// Constructor chaining.
public Motorcycle()
{
Console.WriteLine("In default ctor");
}
public Motorcycle(int intensity)
: this(intensity, "")
{
Console.WriteLine("In ctor taking an int");
}
public Motorcycle(string name)
: this(0, name)
{
Console.WriteLine("In ctor taking a string");
}
// This is the 'master' constructor that does all the real work.
public Motorcycle(int intensity, string name)
{
Console.WriteLine("In master ctor ");
if (intensity > 10)
{
intensity = 10;
}
driverIntensity = intensity;
driverName = name;
}
...
}

Now, ensure your Main() method exercises a Motorcycle object as follows:
static void Main(string[] args)
{
Console.WriteLine("***** Fun with class Types *****\n");
// Make a Motorcycle.
Motorcycle c = new Motorcycle(5);
c.SetDriverName("Tiny");
c.PopAWheely();
Console.WriteLine("Rider name is {0}", c.driverName);
Console.ReadLine();
}

As you can see, the flow of constructor logic is as follows:
• We create our object by invoking the constructor requiring a single int.
• This constructor forwards the supplied data to the master constructor and provides any
additional startup arguments not specified by the caller.
• The master constructor assigns the incoming data to the object’s field data.
• Control is returned to the constructor originally called, and executes any remaining code
statements.
Great! At this point you are able to define a class with field data (aka member variables) and
various members that can be created using any number of constructors. Next up, let’s formalize

0 comments:

Post a Comment