Constructors and Inheritance


In a hierarchy, it is possible for both base classes and derived classes to have their own constructors. This raises an important question: what constructor is responsible for building an object of the derived class? The one in the base class, the one in the derived class, or both? The answer: the constructor for the base class constructs the base class portion of the object, and the constructor for the derived class constructs the derived class part. This makes sense because the base class has no knowledge of or access to any element in a derived class. Thus, their construction must be separate. The preceding examples have relied upon the default constructors created automatically by C#, so this was not an issue. However, in practice, most classes will define constructors. Here you will see how to handle this situation.

When only the derived class defines a constructor, the process is straightforward: simply construct the derived class object. The base class portion of the object is constructed automatically using its default constructor. For example, here is a reworked version of Triangle that defines a constructor. It also makes style private since it is now set by the constructor.

 // Add a constructor to Triangle. using System; // A class for two-dimensional objects. class TwoDShape {   double pri_width;  // private   double pri_height; // private   // properties for width and height.   public double width {      get { return pri_width; }      set { pri_width = value; }   }   public double height {      get { return pri_height; }      set { pri_height = value; }   }   public void showDim() {     Console.WriteLine("Width and height are " +                        width + " and " + height);   } } // A derived class of TwoDShape for triangles. class Triangle : TwoDShape {   string style; // private   // Constructor   public Triangle(string s, double w, double h) {     width = w;  // init the base class     height = h; // init the base class     style = s;  // init the derived class   }   // Return area of triangle.   public double area() {     return width * height / 2;   }   // Display a triangle's style.   public void showStyle() {     Console.WriteLine("Triangle is " + style);   } } class Shapes3 {   public static void Main() {     Triangle t1 = new Triangle("isosceles", 4.0, 4.0);     Triangle t2 = new Triangle("right", 8.0, 12.0);     Console.WriteLine("Info for t1: ");     t1.showStyle();     t1.showDim();     Console.WriteLine("Area is " + t1.area());     Console.WriteLine();     Console.WriteLine("Info for t2: ");     t2.showStyle();     t2.showDim();     Console.WriteLine("Area is " + t2.area());   } }

Here, Triangle’s constructor initializes the members of TwoDShape that it inherits along with its own style field.

When both the base class and the derived class define constructors, the process is a bit more complicated because both the base class and derived class constructors must be executed. In this case you must use another of C#’s keywords: base, which has two uses. The first use is to call a base class constructor. The second is to access a member of the base class that has been hidden by a member of a derived class. Here, we will look at its first use.

Calling Base Class Constructors

A derived class can call a constructor defined in its base class by using an expanded form of the derived class’ constructor declaration and the base keyword. The general form of this expanded declaration is shown here:

 derived-constructor(parameter-list) : base(arg-list) {    // body of constructor }

Here, arg-list specifies any arguments needed by the constructor in the base class. Notice the placement of the colon.

To see how base is used, consider the version of TwoDShape in the following program. It defines a constructor that initializes the width and height properties. This means that Triangle must explicitly execute the TwoDShape constructor.

 // Add constructors to TwoDShape. using System; // A class for two-dimensional objects. class TwoDShape {   double pri_width;  // private   double pri_height; // private   // Constructor for TwoDShape.   public TwoDShape(double w, double h) {     width = w;     height = h;   }   // properties for width and height.   public double width {      get { return pri_width; }      set { pri_width = value; }   }   public double height {      get { return pri_height; }      set { pri_height = value; }   }   public void showDim() {     Console.WriteLine("Width and height are " +                        width + " and " + height);   } }  // A derived class of TwoDShape for triangles. class Triangle : TwoDShape {   string style; // private   // Call the base class constructor.   public Triangle(string s, double w, double h) : base(w, h) {     style = s;   }   // Return area of triangle.   public double area() {     return width * height / 2;   }   // Display a triangle's style.   public void showStyle() {     Console.WriteLine("Triangle is " + style);   } } class Shapes4 {   public static void Main() {     Triangle t1 = new Triangle("isosceles", 4.0, 4.0);     Triangle t2 = new Triangle("right", 8.0, 12.0);     Console.WriteLine("Info for t1: ");     t1.showStyle();     t1.showDim();     Console.WriteLine("Area is " + t1.area());     Console.WriteLine();     Console.WriteLine("Info for t2: ");     t2.showStyle();     t2.showDim();     Console.WriteLine("Area is " + t2.area());   } }

Notice that the Triangle constructor is now declared as shown here:

 public Triangle(string s, double w, double h) : base(w, h) {

In this version, Triangle( ) calls base with the parameters w and h. This causes the TwoDShape( ) constructor to be called, which initializes width and height using these values. Triangle no longer initializes these values itself. It need only initialize the value unique to it: style. This leaves TwoDShape free to construct its subobject in any manner that it so chooses. Furthermore, TwoDShape can add functionality about which existing derived classes have no knowledge, thus preventing existing code from breaking.

Any form of constructor defined by the base class can be called by base. The constructor executed will be the one that matches the arguments. For example, here are expanded versions of both TwoDShape and Triangle that include default constructors and constructors that take one argument.

 // Add more constructors to TwoDShape. using System; class TwoDShape {   double pri_width;  // private   double pri_height; // private   // Default constructor.   public TwoDShape() {     width = height = 0.0;   }   // Constructor for TwoDShape.   public TwoDShape(double w, double h) {     width = w;     height = h;   }   // Construct object with equal width and height.   public TwoDShape(double x) {     width = height = x;   }   // Properties for width and height.   public double width {      get { return pri_width; }      set { pri_width = value; }   }   public double height {      get { return pri_height; }      set { pri_height = value; }   }   public void showDim() {     Console.WriteLine("Width and height are " +                        width + " and " + height);   } } // A derived class of TwoDShape for triangles. class Triangle : TwoDShape {   string style; // private   /* A default constructor. This automatically invokes      the default constructor of TwoDShape. */   public Triangle() {     style = "null";   }   // Constructor that takes three arguments.   public Triangle(string s, double w, double h) : base(w, h) {     style = s;   }   // Construct an isosceles triangle.   public Triangle(double x) : base(x) {     style = "isosceles";   }   // Return area of triangle.   public double area() {     return width * height / 2;   }   // Display a triangle's style.   public void showStyle() {     Console.WriteLine("Triangle is " + style);   } } class Shapes5 {   public static void Main() {     Triangle t1 = new Triangle();     Triangle t2 = new Triangle("right", 8.0, 12.0);     Triangle t3 = new Triangle(4.0);     t1 = t2;     Console.WriteLine("Info for t1: ");     t1.showStyle();     t1.showDim();     Console.WriteLine("Area is " + t1.area());     Console.WriteLine();     Console.WriteLine("Info for t2: ");     t2.showStyle();     t2.showDim();     Console.WriteLine("Area is " + t2.area());     Console.WriteLine();     Console.WriteLine("Info for t3: ");     t3.showStyle();     t3.showDim();     Console.WriteLine("Area is " + t3.area());     Console.WriteLine();   } }

Here is the output from this version:

 Info for t1: Triangle is right Width and height are 8 and 12 Area is 48 Info for t2: Triangle is right Width and height are 8 and 12 Area is 48 Info for t3: Triangle is isosceles Width and height are 4 and 4 Area is 8

Let’s review the key concepts behind base. When a derived class specifies a base clause, it is calling the constructor of its immediate base class. Thus, base always refers to the base class immediately above the calling class. This is true even in a multileveled hierarchy. You pass arguments to the base constructor by specifying them as arguments to base. If no base clause is present, then the base class’ default constructor is called automatically.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net