C# Coding Solutions—Implementing Interfaces

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
C# Articles

C# Coding Solutions

© 2006 Christian Gross

Implementing Interfaces

The Bridge pattern is used to decouple a class that consumes and a class that implements logic. The purpose of the Bridge pattern is to be able to separate intention from implementation. Technically the Bridge pattern is implemented using interfaces and classes that implement interfaces. A class that consumes manipulates the interface, which results in the consuming class not knowing what the implementation is. Changing the implementation has no ramifications on the consuming class because the consumer is using an interface that has not changed. If we call the consumer a client, and the class implementing an interface the server, the Bridge pattern has decoupled the client from the server.

Using .NET the Bridge pattern can be implemented in multiple variations. What makes each variation different is how a type is declared using details like scope. The following source code illustrates the simplest example of a server that is implementing an interface:

public interface ISimple {
    void Method();
}
 
class SimpleImplement : ISimple {
    public void Method() {
    }
}

In the example the interface ISimple exposes a single method Method. The class SimpleImplement implements ISimple and exposes the method Method. The code is simple, and does what is expected of it. In a nutshell, this is the proverbial Hello World implementation of the Bridge pattern.

Looking at the code example, notice the following details:

  • The interface ISimple is declared as public, which means it is accessible from an external assembly.
  • The class SimpleImplement is not public, and thus it is not accessible from an external assembly.
  • The method SimpleImplement.Method is declared as public and has no scope modifiers such as virtual, new, or override.

The ramifications of the details are that ISimple can be exposed in an assembly and publicly accessed, but SimpleImplement cannot. This behavior is desired because we want external clients knowing the implementation details of SimpleImplement. The client only needs to call some functionality that will instantiate SimpleImplement and return an interface reference. However, in the context of an assembly, SimpleImplement could be directly instantiated and all methods could be called.

What if SimpleImplement did not want to expose Method publicly? Let’s say that SimpleImplement wanted to expose Method only through the interface ISimple. You may want this behavior so that no client within an assembly or external to the assembly had direct references to the class methods. To avoid exposing a method publicly the public keyword is removed. Removing the public keyword generates a compiler error because ISimple.Method has not been defined and implemented breaking the contract of the interface. Another solution is to explicitly associate Method with the implementation of ISimple.Method, as in the following example:

class SimpleImplement : ISimple {
    void ISimple.Method() {
    }
}

With the new declaration Method has been associated with ISimple and can be referenced using only an interface reference as illustrated by the following code:

ISimple simple = new SimpleImplement();
simple.Method();

The new declaration of Method is so foolproof that even SimpleImplement cannot access Method without a typecast to ISimple, as illustrated in the following source code:

class SimpleImplement : ISimple {
    void ISimple.Method() {
    }
    public void AnotherMethod() {
        ((ISimple)this).Method(); // <-- This is ok
        Method(); // <--Causes a compiler method not found error
    }
}

In AnotherMethod the this reference is typecast to ISimple, enabling the calling of the method ISimple.Method.

It would seem silly to force SimpleImplement to perform a typecast of itself so that it can call Method. But there is another reason why you would want to use interface-based addressing on an interface; it relates to the situation when you want a class to implement two interfaces that happen to have identical method names, as in the following source code:

public interface ISimple {
    void Method();
}
public interface IAnother {
    void Method();
}
 
class SimpleImplement : ISimple, IAnother {
    void ISimple.Method() {
    }
    void IAnother.Method() {
    }
}

In the example there are two interface declarations (ISimple and IAnother). Both interface declarations expose the same method, Method. When SimpleImplement implements the interfaces ISimple and IAnother, the default declaration of using a single public method Method will wire Method for both interfaces. To distinguish which implementation of Method is called for which interface, the interface identifier is prefixed to the method name, and the method is made private.

When interfaces have numerous methods or properties, they are tedious to implement for classes that only completely implement a fraction of the interface. The other methods and properties are either default implementations or empty declarations. For example, when implementing the Decorator, State, or Strategy pattern there is functionality that you don’t want to reimplement for each class. The object-oriented approach is to create a default base class that is then subclassed. The default base class implements the interface, and your class implements only the methods that you need it to. Following is an interface declaration that has two methods where one method requires a default implementation and the other not:

public interface IFunctionality {
    void Method();
    string GetIdentifier();
}

The interface IFunctionality has defined two methods: Method and GetIdentifier. The purpose of Method is to represent a method that all derived classes implement. The purpose of GetIdentifier is to retrieve the name of the class that implements IFunctionality and is identified as the method that could have a default implementation for all classes that implement the interface. You can use the following code to implement GetIdentifier:

this.GetType().FullName;

If 15 classes will be implementing the IFunctionality interface, then 15 classes have to implement GetIdentifier using this line of code. But that is excessive coding, and you can optimize it by using an abstract base class that is subclassed by the classes that want to implement IFunctionality. The following is a sample implementation:

abstract class DefaultFunctionality : IFunctionality {
    public void Method() {
        throw new NotImplementedException();
    }
    public string GetIdentifier() {
        return this.GetType().FullName;
    }
}
class MyFunctionality : DefaultFunctionality {
    public void Method() {
        Console.WriteLine( "My own stuff");
    }
}

In the example the class DefaultFunctionality is declared as an abstract base class that implements IFunctionality. The base class is declared as abstract so that it can never be instantiated, because it is a partial implementation of the interface IFunctionality. A rule of thumb is that whenever class implementations are incomplete, mark them as abstract. Any class that subclasses DefaultFunctionality decides what should be implemented. Though the base class is declared as abstract, technically it is necessary to completely implement IFunctionality including the unknown Method; otherwise, the compiler generates an error. The base class method implements Method by throwing an exception, indicating that some subclass must implement the method. Of course, the proper way would have declared the method as abstract, but we did not for a reason that will become apparent shortly. The derived class MyFunctionality inherits the Method and GetIdentifier functionalities, and defines a custom implementation of Method.

Having our hierarchy, let’s use the individual classes and see what happens:

MyFunctionality instDerived = new MyFunctionality();
DefaultFunctionality instBase = instDerived;
IFunctionality instInterface = instDerived;
Console.WriteLine("Type is (" + instInterface.GetIdentifier() + ")");
Console.WriteLine("Calling the interface");
instInterface.Method();
Console.WriteLine("Calling the derived");
instDerived.Method();
Console.WriteLine("Calling the base class");
instBase.Method();

The idea behind the illustrated code is to explicitly call each method associated with each type and see what response is generated. In particular, there are two responses: "My own stuff" or an exception. The instantiated type MyFunctionality is assigned to instDerived. Then instDerived is downcast to base class DefaultFunctionality (instBase) and interface IFunctionality (instInterface). Then Method is called for each variable. Compiling the classes, interfaces, and calling code generates the following warning:

ImplementingInterfaces.cs(32,17): warning CS0108: MyFunctionality.Method()' hides inherited member DefaultFunctionality.Method()'. Use the new keyword if hiding was intended.

This is a very common warning message, and it says that MyFunctionality.Method hides the method DefaultFunctionality.Method. From an interface perspective, that seems acceptable because we are not interested in having the method DefaultFunctionality.Method called. Based on this warning it seems that generated output from the calling sequence should be similar to the following:

Type is (MyFunctionality)
Calling the interface
My own stuff
Calling the derived
My own stuff
Calling the base class
<<Exception>>

Yet when we run the code the generated output is as follows:

Type is (MyFunctionality)
Calling the interface
System.NotImplementedException: The method or operation is not implemented.

The output is particularly interesting because when IFunctionality.Method is called, DefaultFunctionality.Method is called—not MyFunctionality.Method as we expected. The interface binds its methods and properties to the class that implements the interface. If a class subclasses a class that implements an interface, the hierarchy rules apply to the class and not the interface. This is confusing because in other programming languages, implementing an interface in a class and then subclassing that class should have automatically overridden methods in the class that was subclassed. To be able to overload implemented interface methods, you need to use the following declaration:

abstract class DefaultFunctionality : IFunctionality {
    public virtual void Method() {
        throw new NotImplementedException();
    }
    public string GetIdentifier() {
        return this.GetType().FullName;
    }
}
class MyFunctionality : DefaultFunctionality {
    public override void Method() {
        Console.WriteLine( "My own stuff");
    }
}

In the modified declaration DefaultFunctionality.Method is declared as virtual, meaning that DefaultFunctionality implements the interface method IFunctionality.Method and exposes the method as a candidate for being overloaded. The declaration of MyFunctionality.Method also changes and needs to include the override keyword to indicate that the method should overload the base class method. When you run the code, you get the following correctly generated output:

Type is (MyFunctionality)
Calling the interface
My own stuff
Calling the derived
My own stuff
Calling the base class
My own stuff

Of course, we could have avoided all of these problems by applying the abstract keyword to the method, as in the following source code:

abstract class DefaultFunctionality : IFunctionality {
    public abstract void Method();
    public string GetIdentifier() {
        return this.GetType().FullName;
    }
}

Applying the abstract keyword to Method allows the class to implement an interface method without providing a method implementation. The abstract keyword delegates the implementation to any class that subclasses DefaultFunctionality. The class that implements Method uses the override keyword, creating the same inheritance behavior as using the virtual keyword in the abstract base class.

Using both an interface and an abstract base class is a very common technique. It allows functionality to be shared among multiple class types, and the derived class needs to implement only what concerns it. In the example some methods were not implemented or generated exceptions, but often classes will create default actions that can be overridden.

You now know how to use virtual, override, and abstract keywords, but there is another solution for calling the appropriate method—you can implement the interface when required.

The strategy is to define an abstract base class with the required default functionality. But this time the abstract base class does not implement the interface. The modified definition of DefaultFunctionality would be as follows:

abstract class DefaultFunctionality {
    public void Method() {
        Console.WriteLine( "Default Functionality");
    }
    public string GetIdentifier() {
        return this.GetType().FullName;
    }
}

The class DefaultFunctionality has implemented two methods with some functionality. In this declaration of the abstract base class there are no constraints. The constraints are added by the derived class that implements the interface, as the following code example illustrates:

class MyFunctionality : DefaultFunctionality, IFunctionality {
    public void Method() {
        Console.WriteLine("My own stuff");
    }
}

The class MyFunctionality subclasses DefaultFunctionality and implements the interface IFunctionality. Notice though in the declaration of MyFunctionality the implementation of the method GetIdentifier is missing. When the compiler assembles the code it will look at MyFunctionality and attempt to associate the interface with methods. Missing in MyFunctionality is the method GetIdentifier. The compiler will also look at DefaultFunctionality, find GetIdentifier, and consider the implementation of IFunctionality as complete.

In the example, there was no use of the new, virtual, or override identifiers. In this case, because the interface is associated with MyFunctionality, the priority of finding a method to call is first MyFunctionality, and then DefaultFunctionality. The correct output is generated using the following calls:

IFunctionality instInterface = new MyFunctionality ();
Console.WriteLine("Type is (" + instInterface.GetIdentifier() + ")");
instInterface.Method();

With the latest version of our inheritance hierarchy, we can have our cake and eat it too. An abstract base class provides default implementations when necessary, and we don’t have to use a virtual keyword.

Let’s go back to an original declaration that is illustrated again:

class MyFunctionality : DefaultFunctionality, IFunctionality {
    public void Method() {
        Console.WriteLine("My own stuff");
    }
}
class DerivedMyFunctionality : MyFunctionality {
    public new void Method() {
        Console.WriteLine("Derived Functionality");
    }
}

The way that DerivedMyFunctionality is declared is identical how MyFunctionality originally subclassed DefaultFunctionality. And in our earlier example the generated output generated an exception. Therefore, by instantiating DerivedMyFunctionality, casting to IFunctionality will still call MyFunctionality.Method. However, you can get around that by making DerivedMyFunctionality implement IFunctionality like the following code:

class DerivedMyFunctionality : MyFunctionality, IFunctionality {
    public void Method() {
        Console.WriteLine("Derived Functionality");
    }
}

In this modified class declaration, instantiating DerivedMyFunctionality then casting to IFunctionality and finally calling IFunctionality.Method will call DerivedMyFunctionality.Method. It might seem odd to have multiple classes implement the same interface over and over again. The advantage of this approach is due to the way that .NET resolves methods at runtime. If you were to create an object hierarchy and an interface reference associated with each object, you could pick and choose the appropriate object at which certain methods are being called. The compiler very cleverly will pick and choose the methods when exposing the interface.

When implementing interfaces, consider these rules of thumb:

  • When functionality dictated by an interface needs to be implemented over and over again, use an abstract base class.
  • Do not implement interfaces on abstract base classes, but rather in the derived classes.
  • When creating an inheritance hierarchy, don’t be afraid of implementing the identical interface at different places in the inheritance hierarchy. So long as you instantiate the appropriate object in the inheritance hierarchy, the appropriate methods will be called.
  • Use the virtual, abstract, and override keywords to define default implementations that some derived class can or should override.


Previous_Page_.gif Next_Page_.gif

Personal tools