Using Abstract Classes


Sometimes you will want to create a base class that defines only a generalized form that will be shared by all of its derived classes, leaving it to each derived class to fill in the details. Such a class determines the nature of the methods that the derived classes must implement, but does not, itself, provide an implementation of one or more of these methods. One way this situation can occur is when a base class is unable to create a meaningful implementation for a method. This is the case with the version of TwoDShape used in the preceding example. The definition of area( ) is simply a placeholder. It will not compute and display the area of any type of object.

You will see as you create your own class libraries that it is not uncommon for a method to have no meaningful definition in the context of its base class. You can handle this situation two ways. One way, as shown in the previous example, is to simply have it report a warning message. While this approach can be useful in certain situations—such as debugging—it is not usually appropriate. You may have methods that must be overridden by the derived class in order for the derived class to have any meaning. Consider the class Triangle. It has no meaning if area( ) is not defined. In this case, you want some way to ensure that a derived class does, indeed, override all necessary methods. C#’s solution to this problem is the abstract method.

An abstract method is created by specifying the abstract type modifier. An abstract method contains no body and is, therefore, not implemented by the base class. Thus, a derived class must override it—it cannot simply use the version defined in the base class. As you can probably guess, an abstract method is automatically virtual, and there is no need to use the virtual modifier. In fact, it is an error to use virtual and abstract together.

To declare an abstract method, use this general form:

 abstract type name(parameter-list);

As you can see, no method body is present. The abstract modifier can be used only on normal methods. It cannot be applied to static methods. Properties and indexers can also be abstract.

A class that contains one or more abstract methods must also be declared as abstract by preceding its class declaration with the abstract specifier. Since an abstract class does not define a complete implementation, there can be no objects of an abstract class. Thus, attempting to create an object of an abstract class by using new will result in a compile-time error.

When a derived class inherits an abstract class, it must implement all of the abstract methods in the base class. If it doesn’t, then the derived class must also be specified as abstract. Thus, the abstract attribute is inherited until such time that a complete implementation is achieved.

Using an abstract class, you can improve the TwoDShape class. Since there is no meaningful concept of area for an undefined two-dimensional figure, the following version of the preceding program declares area( ) as abstract inside TwoDShape and TwoDShape as abstract. This, of course, means that all classes derived from TwoDShape must override area( ).

 // Create an abstract class. using System; abstract class TwoDShape {   double pri_width;  // private   double pri_height; // private   string pri_name;   // private   // A default constructor.   public TwoDShape() {     width = height = 0.0;     name = "null";   }   // Parameterized constructor.   public TwoDShape(double w, double h, string n) {     width = w;     height = h;     name = n;   }   // Construct object with equal width and height.   public TwoDShape(double x, string n) {     width = height = x;     name = n;   }   // Construct an object from an object.   public TwoDShape(TwoDShape ob) {     width = ob.width;     height = ob.height;     name = ob.name;   }   // Properties for width, height, and name   public double width {     get { return pri_width; }     set { pri_width = value; }   }   public double height {     get { return pri_height; }     set { pri_height = value; }   }   public string name {     get { return pri_name; }     set { pri_name = value; }   }   public void showDim() {     Console.WriteLine("Width and height are " +                        width + " and " + height);   }   // Now, area() is abstract.   public abstract double area(); } // A derived class of TwoDShape for triangles. class Triangle : TwoDShape {   string style; // private   // A default constructor.   public Triangle() {     style = "null";   }   // Constructor for Triangle.   public Triangle(string s, double w, double h) :     base(w, h, "triangle") {       style = s;   }   // Construct an isosceles triangle.   public Triangle(double x) : base(x, "triangle") {     style = "isosceles";   }   // Construct an object from an object.   public Triangle(Triangle ob) : base(ob) {     style = ob.style;   }   // Override area() for Triangle.   public override double area() {     return width * height / 2;   }   // Display a triangle's style.   public void showStyle() {     Console.WriteLine("Triangle is " + style);   } } // A derived class of TwoDShape for rectangles. class Rectangle : TwoDShape {   // Constructor for Rectangle.   public Rectangle(double w, double h) :     base(w, h, "rectangle"){ }   // Construct a square.   public Rectangle(double x) :     base(x, "rectangle") { }   // Construct an object from an object.   public Rectangle(Rectangle ob) : base(ob) { }   // Return true if the rectangle is square.   public bool isSquare() {     if(width == height) return true;     return false;   }   // Override area() for Rectangle.   public override double area() {     return width * height;   } } class AbsShape {   public static void Main() {     TwoDShape[] shapes = new TwoDShape[4];     shapes[0] = new Triangle("right", 8.0, 12.0);     shapes[1] = new Rectangle(10);     shapes[2] = new Rectangle(10, 4);     shapes[3] = new Triangle(7.0);     for(int i=0; i < shapes.Length; i++) {       Console.WriteLine("object is " + shapes[i].name);       Console.WriteLine("Area is " + shapes[i].area());       Console.WriteLine();     }   } }

As the program illustrates, all derived classes must override area( ) (or also be declared abstract). To prove this to yourself, try creating a derived class that does not override area( ). You will receive a compile-time error. Of course, it is still possible to create an object reference of type TwoDShape, which the program does. However, it is no longer possible to declare objects of type TwoDShape. Because of this, in Main( ) the shapes array has been shortened to 4, and a generic TwoDShape object is no longer created.

One other point: Notice that TwoDShape still includes the showDim( ) method and that it is not modified by abstract. It is perfectly acceptable—indeed, quite common—for an abstract class to contain concrete methods that a derived class is free to use as-is. Only those methods declared as abstract must be overridden by derived classes.




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