Wednesday, March 21, 2012

Inheritance & Polymorphism

When you derive a class from a base class, the derived class will inherit all members of the base class except constructors, though whether the derived class would be able to access those members would depend upon the accessibility of those members in the base class. C# gives us polymorphism through inheritance. Inheritance-based polymorphism allows us to define methods in a base class and override them with derived class implementations. Thus if you have a base class object that might be holding one of several derived class objects, polymorphism when properly used allows you to call a method that will work differently according to the type of derived class the object belongs to.

Consider the following class which we'll use as a base class.

class Animal

{

    public Animal()

    {

        Console.WriteLine("Animal constructor");

    }

    public void Greet()

    {

        Console.WriteLine("Animal says Hello");

    }

    public void Talk()

    {

        Console.WriteLine("Animal talk");

    }

    public virtual void Sing()

    {

        Console.WriteLine("Animal song");

    }

};

Now see how we derive another class from this base class.

 

class Dog : Animal

{

    public Dog()

    {

        Console.WriteLine("Dog constructor");

    }

    public new void Talk()

    {

        Console.WriteLine("Dog talk");

    }

    public override void Sing()

    {

        Console.WriteLine("Dog song");

    }

};

Now try this code out.

 

Animal a1 = new Animal();

a1.Talk();

a1.Sing();

a1.Greet();

 

//Output

Animal constructor

Animal talk

Animal song

Animal says Hello

Okay, that came out just as expected. Now try this code out.

 

Animal a2 = new Dog();

a2.Talk();

a2.Sing();

a2.Greet();

 

//Output

Animal constructor

Dog constructor

Animal talk

Dog song

Animal says Hello

We have an object of type Animal, but it references an object of type Dog. Thus you can see the base class constructor getting called first followed by the derived class constructor. Now we call Talk() and find that the method that's executed is the base class method. That's not surprising when you consider that the object was declared to be of the base type which in our case is Animal. Now when we call Sing(), we find that the derived class method has got called. This is because in the base class the method is prototyped as public virtual void Sing() and in the derived class we have overridden it by using public override void Sing(). In C#, we need to explicitly use the override keyword as opposed to C++ where we didn't have to do that. And finally when we call Greet() the base class method gets called and this is not confusing at all specially since the derived class has not even implemented the method.

Now try the following code out.

 

Dog d1 = new Dog();

d1.Talk();

d1.Sing();

d1.Greet();

 

//Output

Animal constructor

Dog constructor

Dog talk

Dog song

Animal says Hello

Okay, here everything came out as expected. No rude surprises there. The fact that we could invoke the Greet() method is proof of inheritance in C#, not that anyone had any doubts to begin with I guess. Now take a look at this new class we'll be using as a base class for some other classes.

 

class Color

{

    public virtual void Fill()

    {

        Console.WriteLine("Fill me up with color");

    }

    public void Fill(string s)

    {

        Console.WriteLine("Fill me up with {0}",s);

    }

};

Now run this code out.

 

Color c1 = new Color();

c1.Fill();

c1.Fill("red");

 

//Output

Fill me up with color

Fill me up with red

Okay, that went fine, I'd say. Now let's derive a class from this class.

 

class Green : Color

{

    public override void Fill()

    {

        Console.WriteLine("Fill me up with green");

    }

};

Now let's try this code out.

 

Green g1 = new Green();

g1.Fill();

g1.Fill("violet");

 

//Output

Fill me up with green

Fill me up with violet

Well, that went fine too. Thus if you have overloaded methods, you can mark some of them as virtual and override them in the derived class. It's not required that you have to override all the overloads. Now I want to demonstrate some stuff on overloaded constructors. For that we'll use the following base class.

 

class Software

{

    public Software()

    {

        m_x = 100;

    }

    public Software(int y)

    {

        m_x = y;

    }

    protected int m_x;

};

Now we'll derive a class from the above class.

 

class MicrosoftSoftware : Software

{

    public MicrosoftSoftware()

    {

        Console.WriteLine(m_x);

    }

};

Now try this code out

 

MicrosoftSoftware m1 = new MicrosoftSoftware();

//MicrosoftSoftware m2 = new MicrosoftSoftware(300); //won't compile

 

//Output

100

The base class had two overloaded constructors. One that took zero arguments and one that took an int. In the derived class we only have the zero argument constructor. Constructors are not inherited by derived classes. Thus we cannot instantiate a derived class object using the constructor that takes an int as parameter. As you will deduce from the output we got, the base class constructor that called was the default parameter-less constructor. Now take a look at this second derived class.

 

class DundasSoftware : Software

{

    //Here I am telling the compiler which

    //overload of the base constructor to call

    public DundasSoftware(int y) : base(y)

    {

        Console.WriteLine(m_x);

    }

   

    //Here we are telling the compiler to first

    //call the other overload of the constructor

    public DundasSoftware(string s, int f) : this(f)

    {

        Console.WriteLine(s);

    }

};

Here we have two constructors, one that takes an int and one that takes a string and an int. Now lets try some code out.

 

DundasSoftware du1 = new DundasSoftware(50);

 

//Output

50

 

DundasSoftware du2 = new DundasSoftware("test",75);

 

//Output

75

test

There, now that you've seen how it came out, things are a lot clearer I bet. You can use the this and base access keywords on other methods too, and not just on constructors.

 

 

Abstract and Virtual Methods

When a base class declares a method as virtual, a derived class can override the method with its own implementation. If a base class declares a member as abstract, that method must be overridden in any non-abstract class that directly inherits from that class. If a derived class is itself abstract, then it inherit abstract members without implementing them. Abstract and virtual members are the basis for polymorphism, which is the second primary characteristic of object-oriented programming. For more information, see Polymorphism (C# Programming Guide).

clip_image004 Abstract Base Classes

You can declare a class as abstract if you want to prevent direct instantiation by means of the new keyword. If you do this, the class can be used only if a new class is derived from it. An abstract class can contain one or more method signatures that themselves are declared as abstract. These signatures specify the parameters and return value but have no implementation (method body). An abstract class does not have to contain abstract members; however, if a class does contain an abstract member, the class itself must be declared as abstract. Derived classes that are not abstract themselves must provide the implementation for any abstract methods from an abstract base class. For more information, see Abstract and Sealed Classes and Class Members (C# Programming Guide) and Abstract Class Design.

clip_image004[1] Interfaces

An interface is a reference type that is somewhat similar to an abstract base class that consists of only abstract members. When a class derives from an interface, it must provide an implementation for all the members of the interface. A class can implement multiple interfaces even though it can derive from only a single direct base class.

Interfaces are used to define specific capabilities for classes that do not necessarily have an "is a" relationship. For example, the IEquatable[`1] interface can be implemented by any class or struct that has to enable client code to determine whether two objects of the type are equivalent (however the type defines equivalence). IEquatable<(Of <(T>)>) does not imply the same kind of "is a" relationship that exists between a base class and a derived class (for example, a Mammal is an Animal). For more information, see Interfaces (C# Programming Guide).

clip_image004[2] Derived Class Access to Base Class Members

A derived class has access to the public, protected, internal, and protected internal members of a base class. Even though a derived class inherits the private members of a base class, it cannot access those members. However, all those private members are still present in the derived class and can do the same work they would do in the base class itself. For example, suppose that a protected base class method accesses a private field. That field has to be present in the derived class in order for the inherited base class method to work properly.

clip_image004[3] Preventing Further Derivation

A class can prevent other classes from inheriting from it, or from any of its members, by declaring itself or the member as sealed. For more information, see Abstract and Sealed Classes and Class Members (C# Programming Guide).

clip_image004[4] Derived Class Hiding of Base Class Members

A derived class can hide base class members by declaring members with the same name and signature. The new modifier can be used to explicitly indicate that the member is not intended to be an override of the base member. The use of new is not required, but a compiler warning will be generated if new is not used. For more information, see Versioning with the Override and New Keywords (C# Programming Guide) and Knowing When to Use Override and New Keywords (C# Programming Guide).

 

 

Polymorphism (C# Programming Guide)

Through inheritance, a class can be used as more than one type; it can be used as its own type, any base types, or any interface type if it implements interfaces. This is called polymorphism. In C#, every type is polymorphic. Types can be used as their own type or as a Object instance, because any type automatically treats Object as a base type.

Polymorphism is important not only to the derived classes, but to the base classes as well. Anyone using the base class could, in fact, be using an object of the derived class that has been cast to the base class type. Designers of a base class can anticipate the aspects of their base class that are likely to change for a derived type. For example, a base class for cars might contain behavior that is subject to change when the car in question is a minivan or a convertible. A base class can mark those class members as virtual, allowing derived classes representing convertibles and minivans to override that behavior.

For more information, see Inheritance.

Polymorphism Overview

When a derived class inherits from a base class, it gains all the methods, fields, properties and events of the base class. To change the data and behavior of a base class, you have two choices: you can replace the base member with a new derived member, or you can override a virtual base member.

Replacing a member of a base class with a new derived member requires the new keyword. If a base class defines a method, field, or property, the new keyword is used to create a new definition of that method, field, or property on a derived class. The new keyword is placed before the return type of a class member that is being replaced. For example:

public class BaseClass

{

    public void DoWork() { }

    public int WorkField;

    public int WorkProperty

    {

        get { return 0; }

    }

}

 

public class DerivedClass : BaseClass

{

    public new void DoWork() { }

    public new int WorkField;

    public new int WorkProperty

    {

        get { return 0; }

    }

}

When the new keyword is used, the new class members are called instead of the base class members that have been replaced. Those base class members are called hidden members. Hidden class members can still be called if an instance of the derived class is cast to an instance of the base class. For example:

 

DerivedClass B = new DerivedClass();

B.DoWork();  // Calls the new method.

 

BaseClass A = (BaseClass)B;

A.DoWork();  // Calls the old method.

In order for an instance of a derived class to completely take over a class member from a base class, the base class has to declare that member as virtual. This is accomplished by adding the virtual keyword before the return type of the member. A derived class then has the option of using the override keyword, instead of new, to replace the base class implementation with its own. For example:

 

public class BaseClass

{

    public virtual void DoWork() { }

    public virtual int WorkProperty

    {

        get { return 0; }

    }

}

public class DerivedClass : BaseClass

{

    public override void DoWork() { }

    public override int WorkProperty

    {

        get { return 0; }

    }

}

Fields cannot be virtual; only methods, properties, events and indexers can be virtual. When a derived class overrides a virtual member, that member is called even when an instance of that class is being accessed as an instance of the base class. For example:

 

DerivedClass B = new DerivedClass();

B.DoWork();  // Calls the new method.

 

BaseClass A = (BaseClass)B;

A.DoWork();  // Also calls the new method.

Virtual methods and properties allow you to plan ahead for future expansion. Because a virtual member is called regardless of which type the caller is using, it gives derived classes the option to completely change the apparent behavior of the base class.

Virtual members remain virtual indefinitely, no matter how many classes have been declared between the class that originally declared the virtual member. If class A declares a virtual member, and class B derives from A, and class C derives from B, class C inherits the virtual member, and has the option to override it, regardless of whether class B declared an override for that member. For example:

 

public class A

{

    public virtual void DoWork() { }

}

public class B : A

{

    public override void DoWork() { }

}

 

public class C : B

{

    public override void DoWork() { }

}

A derived class can stop virtual inheritance by declaring an override as sealed. This requires putting the sealed keyword before the override keyword in the class member declaration. For example:

 

public class C : B

{

    public sealed override void DoWork() { }

}

In the previous example, the method DoWork is no longer virtual to any class derived from C. It is still virtual for instances of C, even if they are cast to type B or type A. Sealed methods can be replaced by derived classes using the new keyword, as the following example shows:

 

public class D : C

{

    public new void DoWork() { }

}

In this case, if DoWork is called on D using a variable of type D, the new DoWork is called. If a variable of type C, B, or A is used to access an instance of D, a call to DoWork will follow the rules of virtual inheritance, routing those calls to the implementation of DoWork on class C.

A derived class that has replaced or overridden a method or property can still access the method or property on the base class using the base keyword. For example:

 

public class A

{

    public virtual void DoWork() { }

}

public class B : A

{

    public override void DoWork() { }

}

 

public class C : B

{

    public override void DoWork()

    {

        // Call DoWork on B to get B's behavior:

        base.DoWork();

 

        // DoWork behavior specific to C goes here:

        // ...

    }

}

For more information, see base.

Note

It is recommended that virtual members use base to call the base class implementation of that member in their own implementation. Letting the base class behavior happen allows the derived class to concentrate on implementing behavior specific to the derived class. If the base class implementation is not called, it is up to the derived class to make their behavior compatible with the behavior of the base class.

No comments:

Post a Comment