Chapter 5. Interfaces and Contracts

During your years as a software developer, you've likely come across the notion of interface-based programming. If you're familiar with the seminal book, Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (known as the "Gang of Four"),[13] then you know that many design patterns employ interface-style "contracts." If you're not familiar with that book and its concepts, I urge you to read it. In this chapter, it is my goal to show you how you can model well-defined, versioned contracts using interfaces. In this context, a contract is an agreement by a type to support a set of functionality.

If you've done any COM or CORBA development over the years, then you've most definitely been doing interface-based development. In fact, the interface is the only form of communication between components in COM. Therefore, much of the design complexity rests in developing solid interfaces before you write any lines of implementation code. Failure to follow this paradigm has been the source of many problems. For example, Visual Studio 2003 offered an easy environment from which you could create web services. By simply annotating methods of a class a certain way, you could expose those methods as methods of the web service. However, the IDE fostered an approach whereby the interface was the result of annotating methods on a class rather than the other way around. Thus, the cart was put before the horse. Instead, you should clearly define the web service interface before doing any coding, and then code the implementation to implement the interface. To name just one benefit of this approach, you can code the client and the server concurrently rather than one after the other. Another part of the problem is that once an interface is published to the world, you cannot change it. Doing so would break all implementations based upon it. Unfortunately, the Visual Studio environment encourages you to break this rule by making it easy for you to add a new method to a class and annotate it as a web service method.

In a well-designed, interface-based system, such as in service-oriented architecture (SOA) systems, you should always design the interface first, as it's the contract between components. The contract drives the implementation rather than the implementation driving, or defining, the contract. Unfortunately, too many tools in the past and even up to the present have promoted this backward development. But just because they promote it does not mean you need to follow their erroneous lead. After all, a contract, when applied to a type, imposes a set of requirements on that type. It makes no sense for the requirements to be driven by the types themselves. In the .NET environment, interfaces are types.

Interfaces Define Types

An interface declaration defines a reference type. Within variables of this type, you can store a reference to an object on the garbage collected heap that implements the contract of the interface type. Each variable in the CLR is stored in a storage location, whether it be on the heap or on the stack. Each of these storage locations has a type associated with it. Therefore, an interface type can describe the type associated with a specific storage location. When a variable—say, a reference to an object—is stored in that location, it must be the same type as the location, or it must be convertible to the type attached to the location. If it can be converted automatically to the type of the location, then it is implicitly convertible to the storage location type. If it requires a specific cast syntax to perform the conversion, then it is explicitly convertible to the storage location type.

Many examples use a fictitious GUI framework as their basis for demonstration purposes, so I'll do the same here. Take a look at the following code snippet:

public interface IUIControl
{
   void Paint();
}

public class Button : IUIControl
{
   public void Paint() {
      // Paint the Button
   }
}

public class ListBox : IUIControl
{
   public void Paint() {
      // Paint the Listbox
   }
}

This example declares an interface named IUIControl that simply exposes one method, Paint. This interface defines a contract, which states that any type that implements this interface must implement the Paint method. Of course, some documentation describing the semantic meaning of what Paint is supposed to do would be nice. For example, you can imagine that an interface named IArtist could have a method named Paint, but the meaning would probably not be reflexive as it is in the previous example—i.e., IUIControl.Paint likely asks a control to paint itself, while IArtist.Paint likely means that the artist should paint something.

Note

I've found it useful to name methods according to both the action they perform and where the action is directed. For example, suppose the IUIControl.Paint method takes a Graphics object as a parameter telling it where to paint itself. In my opinion, it makes the code more readable if the method is named IUIControl.PaintSelfTo. This way, the method call reads like a spoken language in the sense that a method call that looks like control.PaintSelfTo( myGraphicsObject ) is saying, "control, please paint yourself to myGraphicsObject."

Once the classes ListBox and Button in the previous example implement the interface, they can both be treated as type IUIControl. It's handy to consider how the CLR manages the situation. If you were to attempt to store any instance of either Button or ListBox into a variable declared as IUIControl, the operation would succeed. The reference to those concrete types is implicitly convertible to the IUIControl interface type because they both implement the interface. However, to cast an IUIControl reference back into a ListBox or Button reference requires an explicit cast, and that explicit cast could fail at runtime if, in fact, the IUIControl reference does not point to an instance of the desired concrete type.

Defining Interfaces

In the previous section, you got a taste of what a C# interface declaration looks like. It looks similar to a class declaration where the keyword class is simply replaced with the word interface and the methods have no body. Note some important things, though. If you follow the recommended convention, your interface names will start with the letter I. Thus, you can spot interface types in code easily. Interfaces can have an access modifier attached to them. This determines whether the interface type declaration is visible outside the assembly. Most interfaces represent contracts of communication between consumers and providers, so interface declarations are typically declared as public.

Interface members cannot have any access modifiers attached to them. However, they can be decorated with the new modifier, which I discuss later on. Interface members are implicitly public. What would be the point of having a nonpublic interface member when the purpose of the interface is to allow two objects to talk to each other?

What Can Be in an Interface?

Interface declarations may declare zero or more methods, properties, events, and indexers. All are implicitly public and cannot be static. Interfaces may inherit from one or more other interfaces. The syntax is the same as that of class inheritance. When it comes to interfaces, I prefer to think of interface B deriving from interface A as meaning that if you implement interface B, you must also implement interface A. Class inheritance implies an is-a relationship, where the base implementation is also inherited. Even though interface inheritance borrows the same syntax as class inheritance, which is an is-a relationship, it's not completely accurate to consider them one and the same, because interface inheritance merely declares a generalization and no implementation is inherited. Therefore, whenever you say interface inheritance, try to think of it more in terms of an implements relationship. This becomes clearer when I discuss how a derived class can reimplement interfaces and how the compiler does interface implementation mapping in the concrete types that implement the interface.

Here's an example of what you can declare in an interface:

public delegate void DBEvent( IMyDatabase sender );[14]


public interface IMyDatabase : ISerializable, IDisposable
{
   void Insert( object element );
   int Count { get; }
   object this[ int index ] { get; set; }
   event DBEvent Changed;
}

In this example, IMyDatabase also implements ISerializable and IDisposable. Therefore, any concrete type that implements IMyDatabase must also implement ISerializable and IDisposable; otherwise, the concrete type will not compile. If you were to compile this code snippet into an assembly and look at it with ILDASM, you would see that the IMyDatabase type contains nothing more than instance method declarations. Of course, some of those will have special names based on the fact that they're accessors for the property, indexer, or event.

Interface Inheritance and Member Hiding

As mentioned previously, interfaces support multiple inheritance from other interfaces in the syntactic sense. As with multiple inheritance in C++, you may also have diamond-lattice hierarchies, such as in the following code:

public interface IUIControl
{
    void Paint();
}

public interface IEditBox : IUIControl
{
}

public interface IDropList : IUIControl
{
}
public class ComboBox : IEditBox, IDropList
{
    public void Paint() {
        // paint implementation for ComboBox
    }
}

In this example, both the IEditBox and IDropList interfaces implement the IUIControl interface. And because ComboBox implements both of those interfaces, it must implement the union of all the methods declared in the interfaces it directly implements, plus the methods for the interfaces those interfaces implement, etc. In this case, that only includes the IUIControl.Paint method.

Quite simply, all of the methods from all of the interfaces are merged together into one big union to form the set of methods that the concrete class or structure must implement. Therefore, the ComboBox class gets only one implementation of the Paint method. If you were to cast a ComboBox instance into both an IEditBox reference and an IDropList reference, then calling Paint through both of those would call into exactly the same implementation.

Note

If you come from a native C++ background, you might know all of the intricacies of multiple inheritance and diamond-lattice inheritance diagrams and how they relate to virtual inheritance in C++ and the multiple compiler-generated vtables involved. To understand how C# differs, imagine that C# flattens all of those vtables into one table at compile time.

Sometimes—although extremely rarely—you need to declare a method in an interface that hides a method in an inherited interface. You must use the new keyword if you want to keep the compiler from complaining about it with a warning.

Note

Traditionally, Object-Oriented Analysis and Design (OOA-D) considers it bad design to hide a non-virtual inherited member. The implementation that actually gets called depends on the type of reference held, even if the two references point to the same instance. For example, if A.DoWork isn't virtual, and B derives from A and introduces a new B.DoWork that hides the base method, then calling DoWork on a reference to B will call B.DoWork, and calling DoWork on a reference to A obtained by casting a B reference to an A reference will call A.DoWork. This behavior is nonintuitive in object-oriented systems. Just because the language allows you to do something does not mean that it's the correct thing to do. Now you see why the compiler warning exists in the first place.

In the following example, IEditBox, for one reason or another, needs to declare a Paint method whose signature is exactly that of the one in IUIControl. Therefore, it must use the new keyword:

using System;
public interface IUIControl
{
    void Paint();
}

public interface IEditBox : IUIControl
{
    new void Paint();
}

public interface IDropList : IUIControl
{
}

public class ComboBox : IEditBox, IDropList
{
    public void Paint() {
        Console.WriteLine( "ComboBox.IEditBox.Paint()" );
    }
}

public class EntryPoint
{
    static void Main() {
        ComboBox cb = new ComboBox();
        cb.Paint();
        ((IEditBox)cb).Paint();
        ((IDropList)cb).Paint();
        ((IUIControl)cb).Paint();
    }
}

In all calls to the Paint method in the Main method, it always boils down to a call on ComboBox.Paint. That's because the set of required methods that ComboBox must implement are merged together into one set. Both signatures from Paint—the one from IEditBox and the one from IUIControl—are merged into one slot in the requirements list. In the end, they both map to ComboBox.Paint. You can change this behavior by using explicit interface implementation (which I discuss in the section "Explicit Interface Implementation"), where ComboBox can elect to implement two different versions of Paint—one for IEditBox and one for IUIControl.

When the IEditBox interface declares the Paint method using the new keyword, it is said to hide the Paint method declared in IUIControl. When you call ComboBox.Paint, it will invoke the IEditBox.Paint method as if it chose the IEditBox path in the inheritance hierarchy over the IDropList path. In essence, any time any path hides a method, it hides the method for all paths. This will become clearer when I discuss how the compiler matches up a concrete method with an interface method when you call an interface method. That process is called interface mapping, and I cover it in the section titled "Interface Member Matching Rules" later in this chapter.

Implementing Interfaces

When implementing interfaces in C#, you have the choice of implementing them one of two ways. By default, interface implementations are said to be implicit implementations. The method implementations are part of the public contract of the class but also implement the interface implicitly. Alternatively, you can implement the interface explicitly, whereby the method implementations are private to the implementing class and don't become part of the public interface of the class. Explicit implementation provides some flexibility, especially when implementing two interfaces that have methods with the same name in them.

Implicit Interface Implementation

When a concrete type implements the methods in inherited interfaces, and those methods are marked public, it's known as implicit interface implementation. What good is it to say that a concrete type implements the contract of a specific interface if a consumer of the objects of that type cannot call the methods in that contract? For example, the following is not valid:

public interface IUIControl
{
   void Paint();
}

public class StaticText : IUIControl
{
   void Paint();   // !!! WON'T COMPILE !!!
}

If you try to compile this, the compiler will immediately complain that the StaticText class doesn't implement all of the methods of the derived interfaces—in this case, IUIControl. In order for this to work, you could rewrite it as in the following:

public interface IUIControl
{
   void Paint();
}

public class StaticText : IUIControl
{
   public void Paint();   //Notice that we've added 'public' to the method declaration
}

Now, not only will the code compile, but when you call Paint through a reference to StaticText or through a reference to IUIControl, the StaticText.Paint method will be called. Thus, consumers can treat instances of StaticText polymorphically as instances of type IUIControl.

Explicit Interface Implementation

When a concrete type implements an interface implicitly, the interface methods also become part of the public contract of the concrete type itself. However, you might not always want the interface method implementations to become part of the public contract of the class that implements the interface. For example, the System.IO.FileStream class implements IDisposable, but you cannot call Dispose through an instance of FileStream. Instead, you must first cast the reference to the FileStream object to an IDisposable interface, and then you may call Dispose. When you need this behavior in your own types, you must implement the interfaces using explicit interface implementation.

Note

To achieve the same result as Dispose using a reference to a FileStream object, you must call FileStream.Close. In the implementation of FileStream.Close calls straight through to the internal implementation of the Dispose method. Why did the designers of FileStream do this? Most likely because it makes more linguistic sense to "close" a file rather than "dispose of" it.

You can also use explicit implementation to provide separate implementations for overlapping methods in inherited interfaces. Let's look again at the ComboBox example from the previous section. If you want to provide a separate implementation for IEditBox.Paint and IUIControl.Paint inside ComboBox, you can do that using explicit interface implementation, as shown here:

using System;

public interface IUIControl
{
    void Paint();
}

public interface IEditBox : IUIControl
{
    new void Paint();
}

public interface IDropList : IUIControl
{
}

public class ComboBox : IEditBox, IDropList
{
    void IEditBox.Paint() {
        Console.WriteLine( "ComboBox.IEditBox.Paint()" );
    }

    void IUIControl.Paint() {
        Console.WriteLine( "ComboBox.IUIControl.Paint()" );
    }

    public void Paint() {
        ((IUIControl)this).Paint();
    }
}

public class EntryPoint
{
    static void Main() {
        ComboBox cb = new ComboBox();
        cb.Paint();
        ((IEditBox)cb).Paint();
        ((IDropList)cb).Paint();
        ((IUIControl)cb).Paint();
    }
}

Pay attention to the change in syntax. Now, ComboBox has three implementations for Paint. One is specific for the IEditBox interface, the other is specific to the IUIControl interface, and the last one is simply there for convenience to provide a Paint method for the public contract of the ComboBox class. When you implement interface methods explicitly, not only do you add the interface name followed by a dot before the method name, but you also remove the access modifier. This keeps it from being in the public contract for ComboBox. However, the explicit interface implementations aren't exactly private in the sense that you can call them after you cast the instance of the ComboBox to the required interface type. In my implementation of ComboBox.Paint—the one that contributes to the ComboBox public contract—I get to choose which version of Paint to call. In this case, I chose to call IUIControl.Paint. I could just as easily have chosen to implement IEditBox.Paint explicitly and IUIControl.Paint implicitly, and then I wouldn't have needed the third implementation of Paint. But in this case, I believe it adds more flexibility and makes more sense for ComboBox to implement its own Paint method so that it can reuse the other and add value to it at the same time. If you compile and run the previous example, you'll see output similar to the following:

ComboBox.IUIControl.Paint()
ComboBox.IEditBox.Paint()

ComboBox.IUIControl.Paint()
ComboBox.IUIControl.Paint()

Granted, this example is rather contrived, but it's meant to exhibit the intricacies of explicit interface implementation and member hiding during multiple interface inheritance.

Overriding Interface Implementations in Derived Classes

Suppose you have a handy implementation of ComboBox, as in the previous section, and the implementer decided not to seal the class so that you can inherit from it.

Note

I suggest that you declare all classes sealed unless the designer explicitly intends them to be inherited from. In Chapter 4, I explain in detail why this is desired. In short, it's impossible to tell the future.

Now, suppose you create a new class, FancyComboBox, and you want it to paint itself better, maybe with some new psychedelic theme. You could try something like this:

using System;

public interface IUIControl
{
    void Paint();
    void Show();
}

public interface IEditBox : IUIControl
{
    void SelectText();
}

public interface IDropList : IUIControl
{
    void ShowList();
}

public class ComboBox : IEditBox, IDropList
{
    public void Paint() { }
    public void Show() { }

    public void SelectText() { }

    public void ShowList() { }
}

public class FancyComboBox : ComboBox
{
    public void Paint() { }
}

public class EntryPoint
{
    static void Main() {
        FancyComboBox cb = new FancyComboBox();
    }
}

However, the compiler will promptly warn you that FancyComboBox.Paint hides ComboBox.Paint and that you probably meant to use the new keyword. This will surprise you if you were assuming that methods that implement interface methods are automatically virtual. They are not in C#.

Note

Under the covers, interface method implementations are called as if they are virtual methods within the CLR. Any interface method implementations not marked virtual in the C# code are marked as virtual and final (sealed) in the generated IL. If the method is marked virtual in the C# code, then the method is marked with virtual and newslot (new) in the generated IL. This can be the source of some confusion.

When faced with a problem such as this, you have a couple of options. One option is to have FancyComboBox reimplement the IUIControl interface:

using System;

public interface IUIControl
{
    void Paint();
    void Show();
}

public interface IEditBox : IUIControl
{
    void SelectText();
}

public interface IDropList : IUIControl
{
    void ShowList();
}

public class ComboBox : IEditBox, IDropList
{
    public void Paint() {
        Console.WriteLine( "ComboBox.Paint()" );
    }
    public void Show() { }

    public void SelectText() { }

    public void ShowList() { }
}

public class FancyComboBox : ComboBox, IUIControl
{
    public new void Paint() {
        Console.WriteLine( "FancyComboBox.Paint()" );
    }
}

public class EntryPoint
{
    static void Main() {
        FancyComboBox cb = new FancyComboBox();
        cb.Paint();
        ((IUIControl)cb).Paint();
        ((IEditBox)cb).Paint();
}
}

In this example, note a couple of things. First, FancyComboBox lists IUIControl in its inheritance list. That's how you indicate that FancyComboBox is planning to reimplement the IUIControl interface. Had IUIControl inherited from another interface, FancyComboBox would have had to reimplement the methods from those inherited interfaces as well. I also had to use the new keyword for FancyComboBox.Paint, because it hides CombBox.Paint. This wouldn't have been a problem had ComboBox implemented the IUIControl.Paint method explicitly, because it wouldn't have been part of the ComboBox public contract. When the compiler matches class methods to interface methods, it also considers public methods of base classes. In reality, FancyComboBox could have indicated that it reimplements IUIControl but without redeclaring any methods, as the compiler would have just wired up the interface to the base class methods. Of course, doing so would be pointless, because the reason you reimplement an interface in a derived class is to modify behavior.

Note

The ability to reimplement an interface is a powerful one. It highlights the vast differences between the way C# and the CLR handle interfaces and the C++ treatment of interfaces as abstract class definitions. Gone are the intricacies of C++ vtables, as well as the question of when you should use C++ virtual inheritance. As I've said before, and don't mind saying again, C#/CLR interfaces are nothing more than contracts that say, "You, Mr. Concrete Class, agree to implement all of these methods in said contract, a.k.a. interface."

When you implement methods in an interface contract implicitly, they must be publicly accessible. As long as they meet those requirements, they can also have other attributes, including the virtual keyword. In fact, implementing the IUIControl interface in ComboBox using virtual methods as opposed to nonvirtual methods would make the previous problem a lot easier to solve, as demonstrated in the following:

using System;

public interface IUIControl
{
    void Paint();
    void Show();
}

public interface IEditBox : IUIControl
{
    void SelectText();
}

public interface IDropList : IUIControl
{
    void ShowList();
}

public class ComboBox : IEditBox, IDropList
{
public virtual void Paint() {
        Console.WriteLine( "ComboBox.Paint()" );
    }
    public void Show() { }

    public void SelectText() { }

    public void ShowList() { }
}

public class FancyComboBox : ComboBox
{
    public override void Paint() {
        Console.WriteLine( "FancyComboBox.Paint()" );
    }
}

public class EntryPoint
{
    static void Main() {
        FancyComboBox cb = new FancyComboBox();
        cb.Paint();
        ((IUIControl)cb).Paint();
        ((IEditBox)cb).Paint();
    }
}

In this case, FancyComboBox doesn't have to reimplement IUIControl. It merely has to override the virtual ComboBox.Paint method. It's much cleaner for ComboBox to declare Paint virtual in the first place. Any time you have to use the new keyword to keep the compiler from warning you about hiding a method, consider whether the method of the base class should be virtual.

Note

Hiding methods causes confusion and makes code hard to follow and debug. Again, just because the language allows you to do something does not mean that you should.

Of course, the implementer of ComboBox would have had to think ahead and realize that someone might derive from ComboBox, and anticipated these issues. In my opinion, it's best to seal the class and avoid any surprises by people who attempt to derive from your class when you never meant for it to be derived from. Imagine who they will scream at when they encounter a problem. Have you ever used Microsoft Foundation Classes (MFC) in the past and come to a point where you're pulling your hair out because you're trying to derive from an MFC class and wishing a particular method were virtual? In that case, it's easy to blame the designers of MFC for being so flagrantly thoughtless and not making the method virtual when, in reality, it's more accurate to consider the fact that they probably never meant for you to derive from the class in the first place. Chapter 13 describes how containment rather than inheritance is the key in situations like these.

Beware of Side Effects of Value Types Implementing Interfaces

All the examples so far have shown how classes may implement interface methods. In fact, value types can implement interfaces as well. However, there's one major side effect to doing so. If you cast a value type to an interface type, you'll incur a boxing penalty. Even worse, if you modify the value via the interface reference, you're modifying the boxed copy and not the original. Given the intricacies of boxing that I cover in Chapters 4 and 13, you may consider that to be a bad thing.

As an example, consider System.Int32. I'm sure you'll agree that it is one of the most basic types in the CLR. However, you may or may not have noticed that it also implements several interfaces: IComparable, IFormattable, and IConvertible. Consider System.Int32's implementation of IConvertible, for example. All of the methods are implemented explicitly. IConvertible has quite a few methods declared within it. However, none of those are in the public contract of System.Int32. If you want to call one of those methods, you must first cast your Int32 value type into an IConvertible interface reference. Only then may you call one of the IConvertible methods. And of course, because interface-typed variables are references, the Int32 value must be boxed.

Interface Member Matching Rules

Each language that supports interface definitions has rules about how it matches up method implementations with interface methods. The interface member matching rules for C# are pretty straightforward and boil down to some simple rules. However, to find out which method actually gets called at runtime, you need to consider the rules of the CLR as well. These rules are only relevant at compile time. Suppose you have a hierarchy of classes and interfaces. To find the implementation for SomeMethod on ISomeInterface, start at the bottom of the hierarchy and search for the first type that implements the interface in question. In this case, that interface is ISomeInterface. This is the level at which the search for a matching method begins. Once you find the type, recursively move up through the type hierarchy and search for a method with the matching signature, while first giving preference to explicit interface member implementations. If you don't find any, look for public instance methods that match the same signature.

The C# compiler uses this algorithm when matching up method implementations with interface implementations. The method that it picks must be a public instance method or an explicitly implemented instance method, and it may or may not be tagged in C# as virtual. However, when the IL code is generated, all interface method calls are made through the IL callvirt instruction. So, even though the method is not necessarily marked as virtual in the C# sense, the CLR treats interface calls as virtual. Be sure that you don't confuse these two concepts. If the method is marked as virtual in C# and has methods that override it in the types below it, the C# compiler will generate vastly different code at the point of call. Be careful, as this can be quite confusing, as shown by the following contrived example:

using System;

public interface IMyInterface
{
    void Go();
}

public class A : IMyInterface
{
    public void Go() {
        Console.WriteLine( "A.Go()" );
    }
}

public class B : A
{
}

public class C : B, IMyInterface
{
    public new void Go() {
        Console.WriteLine( "C.Go()" );
    }
}

public class EntryPoint
{
    static void Main() {
        B b1 = new B();

        C c1 = new C();
        B b2 = c1;

        b1.Go();
        c1.Go();
        b2.Go();
        ((I)b2).Go();
    }
}

The output from this example is as follows:

A.Go()
C.Go()

A.Go()
C.Go()

The first call, on b1, is obvious, as is the second call on c1. However, the third call, on b2, is not obvious at all. Because the A.Go method is not marked as virtual, the compiler generates code that calls A.Go. The fourth and final call is almost equally confusing, but not if you consider the fact that the CLR handles virtual calls on class type references and calls on interface references significantly differently. The generated IL for the fourth call makes a call to IMyInterface.Go, which, in this case, boils down to a call to C.Go, because b2 is actually a C, and C reimplements IMyInterface.

You have to be careful when searching for the actual method that gets called, because you must consider whether the type of your reference is a class type or an interface type. The C# compiler generates IL virtual method calls in order to call through to interfaces methods, and the CLR uses interface tables internally to achieve this.

Note

C++ programmers must realize that interface tables are different from C++ vtables. Each CLR type only has one method table, whereas a C++ instance of a type may have multiple vtables.

The contents of these interface tables are defined by the compiler using its method-matching rules. For more detailed information regarding these interface tables, see Essential .NET, Volume I: The Common Language Runtime by Don Box and Chris Sells (Boston, MA: Addison-Wesley Professional, 2002), as well as the CLI standard document itself.

The C# method-matching rules explain the situation I discussed previously in the section "Interface Inheritance and Member Hiding." Hiding a method in one hierarchical path of a diamond-shaped hierarchy hides the method in all inheritance paths. The rules state that when you walk up the hierarchy, you short-circuit the search once you find a method at a particular level. These simple rules also explain how interface reimplementation can greatly affect the method-matching process, thus short-circuiting the compiler's search during its progression up the hierarchy. Let's consider an example of this in action:

using System;

public interface ISomeInterface
{
    void SomeMethod();
}

public interface IAnotherInterface : ISomeInterface
{
    void AnotherMethod();
}
public class SomeClass : IAnotherInterface
{
    public void SomeMethod() {
        Console.WriteLine( "SomeClass.SomeMethod()" );
    }

    public virtual void AnotherMethod() {
        Console.WriteLine( "SomeClass.AnotherMethod()" );
    }
}

public class SomeDerivedClass : SomeClass
{
    public new void SomeMethod() {
        Console.WriteLine( "SomeDerivedClass.SomeMethod()" );
    }

    public override void AnotherMethod() {
        Console.WriteLine( "SomeDerivedClass.AnotherMethod()" );
    }
}

public class EntryPoint
{
    static void Main() {
        SomeDerivedClass  obj = new SomeDerivedClass();
        ISomeInterface    isi = obj;
        IAnotherInterface iai = obj;

        isi.SomeMethod();
        iai.SomeMethod();
        iai.AnotherMethod();
    }
}

Let's apply the search rules to each method call in Main in the previous example. In all cases, I've implicitly converted an instance of SomeDerivedClass to references of the two interfaces, ISomeInterface and IAnotherInterface. I place the first call to SomeMethod through ISomeInterface. First, walk up the class hierarchy, starting at the concrete type of the reference, looking for the first class that implements this interface or an interface derived from it. Doing so leaves us at the SomeClass implementation, because, even though it does not implement ISomeInterface directly, it implements IAnotherInterface, which derives from ISomeInterface. Thus, we end up calling SomeClass.SomeMethod. You may be surprised that SomeDerivedClass.SomeMethod was not called. But if you follow the rules, you'll notice that you skipped right over SomeDerivedClass, looking for the bottom-most class in the hierarchy that implements the interface. In order for SomeDerivedClass.SomeMethod to be called instead, SomeDerivedClass would need to reimplement ISomeInterface. The second call to SomeMethod through the IAnotherInterface reference follows exactly the same path when finding the matching method.

Things get interesting in the third call in Main, where you call AnotherMethod through a reference to IAnotherInterface. As before, the search begins at the bottom-most class in the hierarchy that implements this interface, inside SomeClass. Because SomeClass has a matching method signature, your search is complete. However, the twist is that the matching method signature is declared virtual. So when the call is made, the virtual method mechanism places execution within SomeDerivedClass.AnotherMethod. It's important to note that AnotherMethod doesn't change the rules for interface method matching, even though it is implemented virtually. It's not until after the interface method has been matched that the virtual nature of the method has an impact on exactly which implementation gets called at runtime.

Note

Interface method matching is applied statically at compile time. Virtual method dispatching happens dynamically at runtime. You should note the difference between the two when trying to determine which method implementation gets invoked.

The output from the previous example code is as follows:

SomeClass.SomeMethod()
SomeClass.SomeMethod()
SomeDerivedClass.AnotherMethod()

Explicit Interface Implementation with Value Types

Many times, you'll encounter general-use interfaces that take parameters in the form of a reference to System.Object. These interfaces are typically general usage, nongeneric interfaces. For example, consider the IComparable interface, which looks like the following:

public interface IComparable
{
      int CompareTo( object obj );
}

Note

NET 2.0 added support for IComparable<T>, which you should always consider using along with IComparable in order to offer greater type safety.

It makes sense that the CompareTo method accepts such a general type, because it would be nice to be able to pass it just about anything to see how the object passed in compares to the one that implements CompareTo. When dealing strictly with reference types, there's really no loss of efficiency here, because conversion to and from System.Object on reference types is free for all practical purposes. But things get a little sticky when you consider value types. Let's look at some code to see the gory details:

using System;

public struct SomeValue : IComparable
{
    public SomeValue( int n ) {
        this.n = n;
    }

    public int CompareTo( object obj ) {
        if( obj is SomeValue ) {
            SomeValue other = (SomeValue) obj;

            return n - other.n;
        } else {
            throw new ArgumentException( "Wrong Type!" );
        }
    }

    private int n;
}

public class EntryPoint
{
    static void Main() {
        SomeValue val1 = new SomeValue( 1 );
        SomeValue val2 = new SomeValue( 2 );

        Console.WriteLine( val1.CompareTo(val2) );
    }
}

In the innocuous call to WriteLine in Main, you see val1 being compared to val2. But look closely at how many boxing operations are required. First, because CompareTo takes an object reference, val2 must be boxed at the point of the method call. Had you implemented the CompareTo method explicitly, you would have needed to cast the val1 value into an IComparable interface, which would incur a boxing penalty. But once you're inside the CompareTo method, the boxing nightmare is still not overdue to the amount of unboxing necessary. Ouch.

Thankfully, you can employ an optimization when SomeValue is compared to certain types. Take, for example, the case where an instance of SomeValue is compared to another SomeValue instance. You can provide a type-safe version of the CompareTo method to get the job done, as shown in the following code:

using System;

public struct SomeValue : IComparable
{
    public SomeValue( int n ) {
        this.n = n;
    }

    int IComparable.CompareTo( object obj ) {
        if( obj is SomeValue ) {
            SomeValue other = (SomeValue) obj;
return n - other.n;
        } else {
            throw new ArgumentException( "Wrong Type!" );
        }
    }

    public int CompareTo( SomeValue other ) {
        return n - other.n;
    }

    private int n;
}

public class EntryPoint
{
    static void Main() {
        SomeValue val1 = new SomeValue( 1 );
        SomeValue val2 = new SomeValue( 2 );

        Console.WriteLine( val1.CompareTo(val2) );
    }
}

In this example, there is absolutely no boxing in the call to CompareTo. That's because the compiler picks the one with the best match for the type. In this case, because you implement IComparable.CompareTo explicitly, there is only one overload of CompareTo in the public contract of SomeValue. But even if IComparable.CompareTo had not been implemented explicitly, the compiler would have still chosen the type-safe version. The typical pattern involves hiding the typeless versions from casual use so that the user must do a boxing operation explicitly. This operation converts the value to an interface reference in order to get to the typeless version.

The bottom line is that you'll definitely want to follow this idiom any time you implement an interface on a value type where you determine that you can define overloads with better type safety than the ones listed in the interface declaration. Avoiding unnecessary boxing is always a good thing, and your users will appreciate your attention to detail and commitment to efficiency.

Versioning Considerations

The concept of versioning is essentially married to the concept of interfaces. When you create, define, and publish an interface, you're defining a contract—or viewed in more rigid terms—a standard. Any time you have a standard form of communication, you must adhere to it so as not to break any clients of that contract. For example, consider the 802.11 standard upon which WiFi devices are based. It's important that access points from one vendor work with devices from as many vendors as possible. This works as long as all of the vendors agree and follow the standard. Can you imagine the chaos that would erupt if a single vendor's WiFi card were the only one that worked at your favorite Pacific Northwest-based coffee shops? It would be pandemonium. Therefore, we have standards.

Now, nothing states that the standard cannot be augmented. Certain manufacturers do just that. In some cases, if you use Manufacturer A's access point with the same manufacturer's wireless card, you can achieve speeds greater than those supported by the standard. However, note that those augmentations only augment, and don't alter, the standard. Similarly, nothing states that a standard cannot be revised. Standards normally have version numbers attached to them, and when they are revised, the version number is modified. Most of the time, devices that implement the new version also support the previous version. Although not required, it's a good move for those manufacturers who want to achieve maximum market saturation. In the 802.11 example, 802.11a, 802.11b, and 802.11g represent the various revisions of the standard.

The point of this example is that you should apply these same rules to your interfaces once you publish them. You don't normally create interfaces unless you're doing so to allow entities to interact with each other using a common contract. So, once you're done with creating that contract, do the right thing and slap a version number on it. You can create your version number in many ways. For new revisions of your interface, you could simply give it a new name—the key point being that you never change the original interface. You've probably already seen exactly the same idiom in use in the COM world. Typically, if someone such as Microsoft, decides they have a good reason to augment the behavior of an interface, you'll find a new interface definition ending with either an Ex suffix or a numeric suffix. At any rate, it's a completely different interface than the previous one, even though the contract of the new interface could inherit the original interface, and the implementations may be shared.

Note

Current design guidelines in wide use suggest that if you need to create an augmented interface based upon another, you shouldn't use the suffix Ex as COM does. Instead, you should follow the interface name with an ordinal. So, if the original interface is ISomeContract, then you should name the augmented interface ISomeContract2.

In reality, if your interface definitions live within a versioned assembly, you may define a newer version of the same interface, even with the same name, in an assembly with the same name but with a new version number. The assembly loader will resolve and load the proper assembly at runtime. However, this practice can become confusing to the developers using your interface, because they now have to be more explicit about which assembly to reference at build time.

Contracts

Many times, you need to represent the notion of a contract when designing an application or a system. A programming contract is no different than any other contract. You usually define a contract to facilitate communication between two types in your design. For example, suppose you have a virtual zoo, and in your zoo, you have animals. Now, an instance of your ZooKeeper needs a way to communicate to the collection of these ZooDweller objects that they should fly to a specific location. Ignoring the fact that they had all better be fairly obedient, they had also better be able to fly. However, not all animals can fly, so clearly not all of the types in the zoo can support this flying contract.

Contracts Implemented with Classes

Let's consider one way to manage the complexity of getting these creatures to fly from one location to the next. First, consider the assumptions that you can make here. Let's say that this Zoo can have only one ZooKeeper. Second, let's assume that you can model the locations within this Zoo by using a simple two-dimensional Point structure. It starts to look as though you can model this system by the following code:

using System;
using System.Collections.ObjectModel;

namespace CityOfShanoo.MyZoo
{

public struct Point
{
    public double X;
    public double Y;
}

public abstract class ZooDweller
{
    public void EatSomeFood() {
        DoEatTheFood();
    }

    protected abstract void DoEatTheFood();
}

public sealed class ZooKeeper
{
    public void SendFlyCommand( Point to ) {
        // Implementation removed for clarity.
    }
}

public sealed class Zoo
{
    private static Zoo theInstance = new Zoo();
    public static Zoo GetInstance() {
        return theInstance;
    }

    private Zoo() {
        creatures = new Collection<ZooDweller>();[15]

        zooKeeper = new ZooKeeper();
    }

    public ZooKeeper ZooKeeper {
        get {
            return zooKeeper;
        }
    }

    private ZooKeeper zooKeeper;
    private Collection<ZooDweller> creatures;
}

}

There can only be one zoo in the CityOfShanoo, thus the Zoo is modeled as a singleton object, and the only way to obtain the instance of the one and only Zoo is to call Zoo.GetInstance. Also, you can get a reference to the ZooKeeper via the Zoo.ZooKeeper property. It is common practice in the .NET Framework to name the property after the custom type that it represents.

Note

The Singleton design pattern is one of the most widely used and well-known design patterns. Essentially, the pattern allows only one instance of its type to exist at one time. Many people still argue about the best way to implement it. Implementation difficulty varies depending on the language you're using. But in general, some static private instance within the type declaration is lazily initialized at the point of first access. The previous implementation of the Zoo class does that, it creates only one instance per application domain, because the static initializer is not called until the type is first accessed through the GetInstance method.

This initial design defines the ZooDweller as an abstract class that implements a method EatSomeFood. The ZooDweller uses the Non-Virtual Interface (NVI) pattern described in Chapter 13, where the virtual method that the concrete type overrides is declared protected rather than public.

It's important to note that the ZooDweller type does, in fact, define a contract even though it is not an interface. The contract, as written, states that any type that derives from ZooDweller must implement EatSomeFood. Any code that uses a ZooDweller instance can be guaranteed that this method is supported.

Note

Notice that an interface is not required in order to define a contract.

So far, this design is missing a key operation, and that is the one commanding the creatures to fly to a destination within the zoo. Clearly, you cannot put a Fly method on the ZooDweller type, because not all animals in the zoo can fly. You must express this contract in a different way.

Interface Contracts

Because not all creatures in the zoo can fly, an interface provides an excellent mechanism for defining the flying contract. Consider the following modifications to the example from the previous section:

public interface IFly
{
    void FlyTo( Point destination );
}

public class Bird : ZooDweller, IFly
{
public void FlyTo( Point destination ) {
        Console.WriteLine( "Flying to ({0}. {1}).",
                           destination );
    }

    protected override void DoEatTheFood() {
        Console.WriteLine( "Eating some food." );
    }
}

Now, using the interface IFly, Bird is defined such that it derives from ZooDweller and implements IFly.

Note

If you intend to have various bird types derive from Bird, and those various birds have different implementations of ToFly, consider using the NVI pattern. You could introduce a protected virtual method named DoFlyTo that the base types override, while having Bird.FlyTo call through to DoFlyTo. Read the section titled "Use the Non-Virtual Interface (NVI) Pattern" in Chapter 13 for more information on why this is a good idea.

Choosing Between Interfaces and Classes

The previous section on contracts shows that you can implement a contract in multiple ways. In the C# and .NET environments, the two main methods are interfaces and classes, where the classes may even be abstract. In the zoo example, it's pretty clear as to when you should use an interface rather than an abstract class to define an interface. However, the choice is not always so clear, so let's consider the ramifications of both methods.

Note

If the zoo example is not as clear with regard to when to use inheritance vs. interface implementation, consider the following. One could just as easily declare a class ZooFlyer derived from ZooDweller and then derive Bird from ZooFlyer. However, what if we were to introduce ZooInsect derived from ZooDweller. How would we then declare ZooFruitFly? After all, C# does not allow multiple inheritance so ZooFruitFly cannot derive from both ZooInsect and ZooFlyer. When you find situations such as these, it is time to reevaluate your class hierarchy as it is probably too complex.

C# supports abstract classes, therefore, you can easily model a contract using abstract classes. But which method is more powerful? And which is more appropriate? These are not easy questions to answer, although the guideline tends to be that you should prefer a class if possible. Let's explore this.

Note

Since COM became so popular, some developers have a false notion that the only way to define a contract is by defining an interface. It's easy to jump to that conclusion when moving from the COM environment to the C# environment, simply because the basic building block of COM is the interface, and C# and .NET support interfaces natively. However, jumping to that conclusion would be perilous to your designs.

If you're familiar with COM and you've created any serious COM projects in the past, you most certainly implemented the COM objects using C++. You probably even used the Active Template Library (ATL) to shield yourself from the intricacies of the mundane COM development tasks. But at the core of it all, how does C++ model COM interfaces? The answer is with abstract classes.

When you implement a contract by defining an interface, you're defining a versioned contract. That means that the interface, once released, must never change, as if it were cast into stone. Sure, you could change it later, but you would not be very popular when all of your clients' code fails to compile with the modified interface. Consider the following example:

public interface IMyOperations
{
    void Operation1();
    void Operation2();
}

// Client class
public class ClientClass : IMyOperations
{
    public void Operation1() { }
    public void Operation2() { }
}

Now, you've released this wonderful IMyOperations interface to the world, and thousands of clients have implemented it. Then, you start getting requests from your clients asking for Operation3 support in your library. It seems like it would be easy enough to simply add Operation3 to the IMyOperations interface, but that would be a terrible mistake. If you add another operation to IMyOperations, then all of a sudden your clients' code won't compile until they implement the new operation. Also, code in another assembly that knows about the newer IMyOperations could attempt to cast a ClientClass instance into an IMyOperations reference and then call Operation3, thus creating a runtime failure. Clearly, you shouldn't modify an already published interface.

Note

Never modify an already publicly published interface declaration.

You could also address this problem by defining a completely new interface, say IMyOperations2. However, ClientClass would need to implement both interfaces in order to get the new behavior, as shown in the following code:

public interface IMyOperations
{
    void Operation1();
    void Operation2();
}

public interface IMyOperations2
{
    void Operation1();
    void Operation2();
    void Operation3();
}

// Client class
public class ClientClass : IMyOperations,
                           IMyOperations2
{
    public void Operation1() { }
    public void Operation2() { }
    public void Operation3() { }
}

public class AnotherClass
{
    public void DoWork( IMyOperations ops ) {
    }
}

Modifying ClientClass to support the new operation from IMyOperations2 isn't terribly difficult, but what about the code that already exists, such as what is shown in AnotherClass? The problem is that the DoWork method accepts a type of IMyOperations. In order to make it to where the new Operation3 method can be called, the prototype of DoWork must change, or the code within it must do a cast to IOperations2, which could fail at runtime. Because you want the compiler to be able to catch as many type bugs as possible, it would be better if you change the prototype of DoWork to accept a type of IMyOperations2.

Note

If you define your original IMyOperations interface within a fully versioned, strongly named assembly, then you can get away with creating a new interface with the same name in a new assembly, as long as the version of the new assembly is different. Although the .NET Framework supports this explicitly, it doesn't mean you should do it without careful consideration, because introducing two IMyOperations interfaces that differ only by version number of the containing assembly could be confusing to your clients.

That was a lot of work just to make a new operation available to clients. Let's examine the same situation, except using an abstract class:

public abstract class MyOperations
{
    public virtual void Operation1() {
}

    public virtual void Operation2() {
    }
}

// Client class
public class ClientClass : MyOperations
{
    public override void Operation1() { }
    public override void Operation2() { }
}

public class AnotherClass
{
    public void DoWork( MyOperations ops ) {
    }
}

MyOperations is a base class of ClientClass. One advantage is that MyOperations can contain default implementations if it wants to. Otherwise, the virtual methods in MyOperations could have been declared abstract. The example also declares MyOperations abstract, because it makes no sense for clients to be able to create instances of MyOperations. Now, let's suppose you want to add a new Operation3 method to MyOperations, and you don't want to break existing clients. You can do this as long as the added operation is not abstract, such that it forces changes on derived types, as shown here:

public abstract class MyOperations
{
    public virtual void Operation1() {
    }

    public virtual void Operation2() {
    }

    public virtual void Operation3() {
        // New default implementation
    }
}

// Client class
public class ClientClass : MyOperations
{
    public override void Operation1() { }
    public override void Operation2() { }
}

public class AnotherClass
{
    public void DoWork( MyOperations ops ) {
        ops.Operation3();
    }
}

Notice that the addition of MyOperations.Operation3 doesn't force any changes upon ClientClass, and AnotherClass.DoWork can make use of Operation3 without making any changes to the method declaration. This technique doesn't come without its drawbacks, though. You're restricted by the fact that the managed runtime only allows a class to have one base class. ClientClass has to derive from MyOperations to get the functionality, therefore, it uses up its only inheritance ticket. This may put complicated restrictions upon your client code. For example, what if one of your clients needs to create an object for use with .NET Remoting? In order to do so, the class must derive from MarshalByRefObject.

Sometimes, it's tricky to find a happy medium when deciding between interfaces and classes. I use the following rules of thumb:

  • If modeling an is-a relationship, use a class: If it makes sense to name your contract with a noun, then you should probably model it with a class.

  • If modeling an IMPLEMENTS relationship, use an interface: If it makes sense to name your contract with an adjective, as if it is a quality, then you should probably model it as an interface.

  • Consider wrapping up your interface and abstract class declarations in a separate assembly: Implementations in other assemblies can then reference this separate assembly.

  • If possible, prefer classes over interfaces: This can be helpful for the sake of extensibility.

You can see examples of these techniques throughout the .NET Framework Base Class Library (BCL). Consider using them in your own code as well.

Summary

This chapter introduced you to interfaces and how you can model a well-defined, versioned contract using an interface. Along with showing you the various ways that classes can implement interfaces, I also described the process that the C# compiler follows when matching up interface methods to implementations in the implementing class. I described interfaces from the perspective of reference types and value types—specifically, how expensive boxing operations can cause you pain when using interfaces on value types. Finally, I spent some time comparing and contrasting the use of interfaces and classes when modeling contracts between types in your design.

In the next chapter, I'll explain the intricacies of operator overloading in the C# language and why you may want to avoid it when creating code used by other .NET languages.



[13] Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Boston, MA: Addison-Wesley Professional, 1995) is cited in the references at the end of this book.

[14] If you're unfamiliar with the delegate keyword and how delegates are used to declare events, don't worry. You'll find a thorough discussion of delegates and events in Chapter 10.

[15] If the syntax of Collection <ZooDweller> looks foreign to you, don't worry. It is a declaration of a collection based on a generic collection type. I will cover generics in detail in Chapter 11.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.236.70