Home

Generic Classes and Inheritance

 

Introduction

Consider the following geometric figures:

Square Rectangle Trapezoid Parallelogram
Square Rectangle Trapezoid Parallelogram

Notice that these are geometric figures with each having four sides. From what we know so far, we can create a base class to prepare it for inheritance. If the class is very general, we can make it a generic one. We can set a data type as an unknown type, anticipating that the dimensions of the figure can be considered as integer or double-precision types. Here is an example:

using System;

namespace Lessons
{
    public class Quadrilateral<T>
    {
        protected T _base;
        protected T _height;
        protected string _name;

        public virtual T Base
        {
            get { return _base; }
            set { _base = value; }
        }

        public virtual T Height
        {
            get { return _height; }
            set { _height = value; }
        }

        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public Quadrilateral()
        {
            _name = "Quadrilateral";
        }

        public Quadrilateral(string name)
        {
            _name = "Quadrilateral";
        }

        public Quadrilateral(T bs, T height)
        {
            _name = "Quadrilateral";
            _base = bs;
            _height = height;
        }

        public Quadrilateral(string name, T bs, T height)
        {
            _name = name;
            _base = bs;
            _height = height;
        }


        public virtual string Describe()
        {
            return "A quadrilateral is a geometric figure with four sides";
        }

        public virtual void ShowCharacteristics()
        {
            Console.WriteLine("Geometric Figure: {0}", Name);
            Console.WriteLine("Description:      {0}", Describe());
            Console.WriteLine("Base:             {0}", Base);
            Console.WriteLine("Height:           {0}", Height);
        }
    }

    class Program
    {
        static int Main()
        {
            // Trapezoid with equal sides
            Quadrilateral<double> Kite =
                new Quadrilateral<double>("Beach Kite", 18.64, 18.64);
            Kite.ShowCharacteristics();
            Console.WriteLine();

            // Rectangle, in meters
            Quadrilateral<Byte> BasketballStadium = new Quadrilateral<Byte>();
            BasketballStadium.Name = "Basketball Stadium";
            BasketballStadium.Base = 15;
            BasketballStadium.Height = 28;
            BasketballStadium.ShowCharacteristics();
            Console.WriteLine();
            return 0;
        }
    }
}

This would produce:

Geometric Figure: Beach Kite
Description:      A quadrilateral is a geometric figure with four sides
Base:             18.64
Height:           18.64

Geometric Figure: Basketball Stadium
Description:      A quadrilateral is a geometric figure with four sides
Base:             15
Height:           28

Press any key to continue . . .

If you have a generic class that can serve as a foundation for another class, you can derive one class from the generic one. To do this, use the formula we apply when deriving a class but follow the name of each class with <>. Inside of the <> operator, enter the same identifier to indicate that the class is a generic type that is based on another generic class. Here is an example:

public class Square<T> : Quadrilateral<T>
{
}

In the body of the new class, you can use the parameter type as you see fit. For example, you can declare some member variables of that type. You can create methods that return the parameter type or you can pass arguments of the parameter type. When implementing the methods of the new class, use the member variables of the parameter and the argument(s) based on the parameter type as you see fit. You can then declare a variable of the class and use it as we done so far for other generic classes. Here is an example:

using System;

namespace Lessons
{
    public class Quadrilateral<T>
    {
        protected T _base;
        protected T _height;
        protected string _name;

        public virtual T Base
        {
            get { return _base; }
            set { _base = value; }
        }

        public virtual T Height
        {
            get { return _height; }
            set { _height = value; }
        }

        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public Quadrilateral()
        {
            _name = "Quadrilateral";
        }

        public Quadrilateral(string name)
        {
            _name = "Quadrilateral";
        }

        public Quadrilateral(T bs, T height)
        {
            _name = "Quadrilateral";
            _base = bs;
            _height = height;
        }

        public Quadrilateral(string name, T bs, T height)
        {
            _name = name;
            _base = bs;
            _height = height;
        }


        public virtual string Describe()
        {
            return "A quadrilateral is a geometric figure with four sides";
        }

        public virtual void ShowCharacteristics()
        {
            Console.WriteLine("Geometric Figure: {0}", Name);
            Console.WriteLine("Description:      {0}", Describe());
            Console.WriteLine("Base:             {0}", Base);
            Console.WriteLine("Height:           {0}", Height);
        }
    }

    public class Square<T> : Quadrilateral<T>
    {
        public Square()
        {
            _name = "Square";
        }
        public Square(string name)
        {
            _name = "Square";
        }
        public Square(T side)
        {
            _name = "Square";
            _base = side;
            _height = side;
        }
        public Square(string name, T side)
        {
            _name = name;
            _base = side;
            _height = side;
        }
        public override string Describe()
        {
            return "A square is a quadrilateral with four equal sides";
        }
        public override void ShowCharacteristics()
        {
            Console.WriteLine("Geometric Figure: {0}", Name);
            Console.WriteLine("Description:      {0}", Describe());
            Console.WriteLine("                  {0}", Describe());
            Console.WriteLine("Side:             {0}", Base);
        }
    }

    class Program
    {
        static int Main()
        {
            // Rectangle, in meters
            Square<Byte> plate = new Square<Byte>();
    	    plate.Name   = "Plate";
	    plate.Base   = 15;
	    plate.Height = 28;
	    plate.ShowCharacteristics();

            Console.WriteLine();
            return 0;
        }
    }
}

This would produce:

Geometric Figure: Plate
Description:      A quadrilateral is a geometric figure with four sides
                  A square is a quadrilateral with four equal sides
Side:             15

Press any key to continue . . .

Generic Classes and Interfaces

As done for a generic class, you can create a generic interface that would serve as the base for generic classes. To proceed, when creating the interface, follow its name with a <> declaration and, inside of the <> operator, enter an identifier for the parameter type. Here is an example:

public interface IGeometry<T>
{
    string Name { get; set; }
    void Display();
}

Since this is a generic interface, like an interface class, when deriving a class from it, follow the formula we reviewed for inheriting from a generic class. Here is an example:

using System;

public interface IGeometry<T>
{
    string Name { get; set; }
    void Display();
}

public class Round<T> : IGeometry<T>
{
}

When implementing the derived class, you must observe all rules that apply to interface derivation. Here is an example:

using System;

public interface IGeometry<T>
{
    string Name { get; set; }
    void Display();
}

public class Round<T> : IGeometry<T>
{
    private string _name;

    public Round()
    {
        _name = "Unknown";
    }
    public Round(string name)
    {
        _name = name;
    }

    public virtual string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public virtual void Display()
    {
        Console.WriteLine("Name: {0}", Name);
    }
}

public class Exercise
{
    static int Main()
    {
        Round<decimal> rnd = new Round<decimal>();

        rnd.Name = "General Round Shape";
        rnd.Display();

        Console.WriteLine();
        return 0;
    }
}

This would produce:

Name: General Round Shape

Press any key to continue . . .

In the same way, you can derive a generic class from another generic class that derived from a generic interface.

Constraining a Generic Class

Imagine you create a regular interface such as the following:

public interface IGeometry
{
    string Name { get; set; }
    void Display();
}

Then imagine you derive a regular class from it. Here is an example:

using System;

public interface IGeometry
{
    string Name { get; set; }
    void Display();
}

public class Round : IGeometry
{
    private string _name;
    private double _rad;

    public Round()
    {
        _name = "Unknown";
    }
    public Round(string name)
    {
        _name = name;
        _rad = 0.00;
    }
    public Round(string name, double radius)
    {
        _name = name;
        _rad = radius;
    }

    public virtual string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public double Radius
    {
        get { return _rad; }
        set
        {
            _rad = (value <= 0) ? 0.00 : value;
        }
    }

    public virtual void Display()
    {
        Console.WriteLine("Name:   {0}", Name);
        Console.WriteLine("Radius: {0}", Radius);
    }
}

public class Exercise
{
    static int Main()
    {
        Round rnd = new Round();

        rnd.Name = "General Round Shape";
        rnd.Radius = 38.24;
        rnd.Display();

        Console.WriteLine();
        return 0;
    }
}

You may be tempted to derive just any type of class from it. One of the features of generics is that you can create a class that must implement the functionality of a certain abstract class of your choice. For example, when creating a generic class, you can oblige it to implement the functionality of a certain interface or you can make sure that the class is derived from a specific base class. This would make sure that the generic class surely contains some useful functionality.

To create a constraint on a generic class, after the <TypeName> operator, type where TypeName : followed by the rule that the class must follow. For example, you may want the generic class to implement the functionality of a pre-defined class. You can create the generic class as follows:

using System;

public interface IGeometry
{
}

public class Round : IGeometry
{
}

public class Sphere<T>
where T : Round
{
}

After creating the class, you must implement the virtual members of the where class/interface, using the rules of generic classes, the way we have done it so far. When declaring a variable for the generic class, in its <> operator, you must enter an object of the base class. Here is an example:

using System;

public interface IGeometry
{
    string Name { get; set; }
    void Display();
}

public class Round : IGeometry
{
    private string _name;
    private double _rad;

    public Round()
    {
        _name = "Unknown";
    }
    public Round(string name)
    {
        _name = name;
        _rad = 0.00;
    }
    public Round(string name, double radius)
    {
        _name = name;
        _rad = radius;
    }

    public virtual string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public double Radius
    {
        get { return _rad; }
        set
        {
            _rad = (value <= 0) ? 0.00 : value;
        }
    }

    public virtual void Display()
    {
        Console.WriteLine("Name:   {0}", Name);
        Console.WriteLine("Radius: {0}", Radius);
    }
}

public class Sphere<T>
where T : Round
{
    private T _t;

    public Sphere() { }
    public Sphere(T fig)
    {
        _t = fig;
    }
    public T Figure
    {
        get { return _t; }
        set { _t = value; }
    }
}

public class Exercise
{
    static int Main()
    {
        Round rnd = new Round();

        rnd.Name = "Circle";
        rnd.Radius = 60.12;

        Sphere<Round> sph = new Sphere<Round>();
        sph.Figure = rnd;
        Console.WriteLine("Circle Characteristics");
        Console.WriteLine("Name:   {0}", sph.Figure.Name);
        Console.WriteLine("Radius: {0}", sph.Figure.Radius);

        Console.WriteLine();
        return 0;
    }
}

This would produce:

Circle Characteristics
Name:   Circle
Radius: 60.12

Press any key to continue . . .

You can also create a constraint so that a generic class implements an interface.

 

 

 


Previous Copyright © 2006 FunctionX, Inc.