C Sharp

Interfaces and Inheritance

Two common problems are associated with interfaces and inheritance. The first problem, illustrated here with a code example, deals with the issue of deriving from a base class that contains a method name identical to the name of an interface method that the class needs to implement.

using System;
public class Control
{
    public void Serialize()
    {
        Console.WriteLine("Control.Serialize called");
    }
}
public interface IDataBound
{
    void Serialize();
}
public class EditBox : Control, IDataBound
{
}
class InterfaceInh1App
{
    public static void Main()
    {
        EditBox edit = new EditBox();
        edit.Serialize();
    }
}

As you know, to implement an interface, you must provide a definition for every member in that interface's declaration. However, in the preceding example we don't do that, and the code still compiles! The reason it compiles is that the C# compiler looks for an implemented Serialize method in the EditBox class and finds one. However, the compiler is incorrect in determining that this is the implemented method. The Serialize method found by the compiler is the Serialize method inherited from the Control class and not an actual implementation of the IDataBound.Serialize method. Therefore, although the code compiles, it will not function as expected, as we'll see next.

Now let's make things a little more interesting. Notice that the following code first checks-via the as operator-that the interface is implemented and then attempts to call an implemented Serialize method. The code compiles and works. However, as we know, the EditBox class doesn't really implement a Serialize method as a result of the IDataBound inheritance. The EditBox already had a Serialize method (inherited) from the Control class. This means that in all likelihood the client is not going to get the expected results.

using System;
public class Control
{
    public void Serialize()
    {
        Console.WriteLine("Control.Serialize called");
    }
}
public interface IDataBound
{
    void Serialize();
}
public class EditBox : Control, IDataBound
{
}
class InterfaceInh2App
{
    public static void Main()
    {
        EditBox edit = new EditBox();
    IDataBound bound = edit as IDataBound;
    if (bound != null)
    {
        Console.WriteLine("IDataBound is supported...");
        bound.Serialize();
    }
    else
    {
        Console.WriteLine("IDataBound is NOT supported...");
    }
    }
}

Another potential problem to watch for occurs when a derived class has a method with the same name as the base class implementation of an interface method. Let's look at that in code as well: -

using System;
interface ITest
{
    void Foo();
}
// Base implements ITest.
class Base : ITest
{
    public void Foo()
    {
        Console.WriteLine("Base.Foo (ITest implementation)");
    }
}
class MyDerived : Base
{
    public new void Foo()
    {
        Console.WriteLine("MyDerived.Foo");
    }
}
public class InterfaceInh3App
{
    public static void Main()
    {
        MyDerived myDerived = new MyDerived();
        myDerived.Foo();
        ITest test = (ITest)myDerived;
        test.Foo();
    }
}

This code results in the following screen output: -

MyDerived.Foo
Base.Foo (ITest implementation)

In this situation, the Base class implements the ITest interface and its Foo method. However, the MyDerived class derives from Base with a new class and implements a new Foo method for that class. Which Foo gets called? It depends on what reference you have. If you have a reference to the MyDerived object, its Foo method will be called. This is because even though the myDerived object has an inherited implementation of ITest.Foo, the run time will execute the MyDerived.Foo because the new keyword specifies an override of the inherited method.

However, when you explicitly cast the myDerived object to the ITest interface, the compiler resolves to the interface implementation. The MyDerived class has a method of the same name, but that's not what the compiler is looking for. When you cast an object to an interface, the compiler traverses the inheritance tree until a class is found that contains the interface in its base list. This is why the last two lines of code in the Main method result in the ITest implemented Foo being called.

Hopefully, some of these potential pitfalls involving name collisions and interface inheritance have supported my strong recommendation: always cast the object to the interface whose member you're attempting to use.