Team LiB
Previous Section Next Section

Interface Programming

Allowing classes to have intimate knowledge of each other goes against the basic principle of object-oriented programming. That principle states that you should try to decouple classes from one another as much as possible. If you don't, you run the risk of creating spaghetti code. To understand what is meant by spaghetti code, imagine a bowl of spaghetti where every noodle represents a different object. As you can see, each class (or noodle) touches quite a few other classes. Therefore, if you change one class, you must revisit, redo, and/or recompile every other class that it touches. That might not be a problem for a small project, but for large projects that need to be maintained, that would be a big problem. One way to reduce the coupling of classes is to use interfaces. In the following sections, you will learn why you should use interfaces and how to implement them.

Understanding the Interface

An interface is essentially a contract. It is a contract to implement the methods and properties that are declared in the contract. In other words, if a class implements an interface, it is agreeing that it will implement all the properties and methods. If the class does not, it has broken the contract and the compiler will complain and will refuse to compile the project as shown in Figure 38.1.

Figure 38.1. A class that does not fully implement the ISomeInterface interface.


TIP

An interface is a contract. It can be a member of a namespace or class and can contain any of the following: methods, properties, indexers, and events. Although an interface is very similar to an abstract base class that contains only abstract methods (for the C++ people, it is similar to a pure virtual method), there are some very important distinctions. In C#, a class can inherit from only one base class, but it can implement multiple interfaces. An interface can inherit from one or more interfaces and can be implemented or realized by either classes or structs.


Figure 38.2 shows a class model of an application that uses interfaces.

Figure 38.2. A class diagram of a sample program that uses interfaces.


In this diagram, you can see two distinct base classes: Animal and Person. You can also see that these two base classes realize the ISpeak interface. Because both species are capable of emitting sounds (speaking), they share a common trait. Other than that, the two species (at least for the purposes of this demo) are completely different. Because both base classes implement the ISpeak interface, you can simply reference both classes by the ISpeak interface and cause the concrete implementations of the interfaces to speak. This will be demonstrated later in this chapter (see Listing 38.2).

Declaring the Interface

An interface is declared in a similar fashion as a C# class. As shown in the MSDN help, an interface is declared in the following manner.

[attributes] [modifiers] interface identifier [:base-list]
{interface-body}[;]

  • attributes The attributes section is optional and can contain any additional declarative information. For more information on using or declaring attributes please refer to Chapter 11, "Reflection and Code Attributes."

  • modifiers The modifiers section is optional. The available modifiers are new, public, protected, internal, internal protected, and private.

  • interface The interface keyword is used to tell the compiler that this is an interface declaration.

  • identifier This is the place to put the name of the interface.

  • :base-list The base-list is an optional section that can contain a list of one or more interfaces from which to inherit. Each base interface is separated by a comma.

  • {interface-body} This section contains the methods, properties, indexers, and events associated with the interface.

Practical Declaration of Interfaces

When declaring an interface, the first thing to do is specify any attributes to be associated with the interface. In the following example, this is demonstrated by the attribute SomeAttribute. Next, you declare the visibility or access modifier, followed by the keyword interface and then the interface name. In the example, the interface is declared as a public interface with a name of ISomeInterface. Then you specify which (if any) interfaces to inherit from. This example shows that the interface inherits from two base interfaces: BaseInterface1 and BaseInterface2. Finally, you specify the methods, properties, indexers, and events associated with this interface.

[SomeAttribute()]
public interface ISomeInterface : BaseInterface1, BaseInterface2
{
  // methods
  // properties
  // indexers
  // events
}

It is customary to begin the interface name with an uppercase I. This is demonstrated in the previous code snippet.

Declaring Methods

When declaring the methods of an interface, you do not specify an access modifier. That is because when they are implemented, all the methods must be public. This is demonstrated in the following code snippet:

public interface ISomeInterface
{
  void SomeMethod();
}

Declaring Properties

Just as with a method, when declaring a property for an interface, do not specify the access modifier. However, you must specify which property accessors must be present. By specifying the accessors, you are indicating whether the interface will be read only, read-write, or write only. For example, the following code snippet specifies that the property SomeProperty will have both a get and set accessor and will therefore be a read-write property:

public interface ISomeInterface
{
  int SomeProperty
  {
    get; set; 
  }
}

Declaring Indexers

An indexer is declared in the same way as a property. The exception is that instead of giving it a name, you must use the name this followed by the formal index parameter list. The following code snippet demonstrates how to declare an indexer in an interface:

public interface ISomeInterface
{
  string this[int index]
  {
    get; set; 
  }
}

Declaring Events

For most of the interface declaration, there have been slight differences. Fortunately, some things don't change. Given the following delegate:

public delegate void CallbackDelegate(object sender, object event);

an event is declared, in a class in the following manner:

event CallbackDelegate OnCallback;

In an interface, an event is declared in the exact same way. For more information on events and delegates, please refer to Chapter 10, "Events and Delegates."

Declaring the Interface Implicitly

To realize an interface from a class or struct, you must first implement all the methods, properties, indexers, and events. To do this, you can choose to implicitly implement the interface members. According to the definition at www.dictionary.com, the word implicit means "implied or understood though not directly expressed." Essentially, that means that all we have to do is implement the members of the interface in a class without doing anything special (actually, you must declare the methods, properties, indexers, and events as public). Listing 38.1 demonstrates how to implement a method of an interface implicitly.

Listing 38.1. Implementing an Interface Implicitly
using System;

namespace ImplicitInterfaceExample
{
  public interface ISpeak
  {
    void Speak();
  }

  public abstract class Animal : ISpeak
  {
    #region ISpeak Members

    public abstract void Speak();

    #endregion
  }

  public abstract class Person : ISpeak
  {
    #region ISpeak Members

    public abstract void Speak();

    #endregion
  }

  public class Dog : Animal
  {
    #region ISpeak Members

    public override void Speak()
    {
      System.Console.WriteLine("Woof!");
    }

    #endregion
  }

  public class Cat : Animal
  {
    #region ISpeak Members

    public override void Speak()
    {
      System.Console.WriteLine("Meow!");
    }

    #endregion
  }

  public class Adult : Person
  {
    #region ISpeak Members

    public override void Speak()
    {
      System.Console.WriteLine("Hello!");
    }

    #endregion
  }

  public class Child : Person
  {
    #region ISpeak Members

    public override void Speak()
    {
      System.Console.WriteLine("<<High Pitched>> Hi!");
    }

    #endregion
  }

  class ImplicitInterfaceExample
  {
    public static void Talk(ISpeak speakingEntity)
    {
      speakingEntity.Speak();
    }

    [STAThread]
    static void Main(string[] args)
    {
      Talk(new Adult());
      Talk(new Child());
      Talk(new Dog());
      Talk(new Cat());

      System.Console.ReadLine();
    }
  }
}

In this example, the Dog, Cat, Adult, and Child classes realize the ISpeak interface. Because of this, they are contractually obligated to implement the Speak method that was specified by the contract (interface). Figure 38.3 shows the output of the program.

Figure 38.3. Output of the ImplicitInterfaceImplementation program.


Overriding a Previous Implementation

In the previous example, you learned how to implicitly implement a method from an interface. However, what happens if you implement an interface from a base class and then in one subclass, you decide that you need to change the implementation?

Your first thought might be to redeclare the method in the subclass with the new keyword. After all, that is how you would do it without using interfaces. This is almost correct. In addition to redeclaring the method in the subclass, you must also explicitly declare that the subclass implements the interface. For example, in Listing 38.2, you redeclare the method in the subclass without explicitly implementing the interface on the subclass. Figure 38.4 shows the result. This is not what was expected.

Listing 38.2. Incorrectly Redeclaring the Interface Method
using System;
namespace OverridingImplicitInterfaceImplementations
{
  public interface ISpeak
  {
    void Speak();
  }

  public class Child : ISpeak
  {
    #region ISpeak Members

    public void Speak()
    {
      System.Console.WriteLine("<<High Pitched>> Hi!");
    }

    #endregion
  }

  public class Adolescent : Child
  {
    #region ISpeak Members

    public new void Speak()
    {
      System.Console.WriteLine("Yo!");
    }

    #endregion
  }

  class ImplicitInterfaceExample
  {
    public static void Talk(ISpeak speakingEntity)
    {
      speakingEntity.Speak();
    }

    [STAThread]
    static void Main(string[] args)
    {
      Talk(new Child());
      Talk(new Adolescent());

      System.Console.ReadLine();
    }
  }
}

Figure 38.4. Output of Listing 38.2.


If you were to explicitly implement the ISpeak interface in the subclass Adolescent as demonstrated in the following code snippet, you would get the desired results. The output of the modified code is shown in Figure 38.5.

public class Adolescent : Child, ISpeak
{
  #region ISpeak Members

  public new void Speak()
  {
    System.Console.WriteLine("Yo!");
  }

  #endregion
}

Figure 38.5. Output of Listing 38.2 with the modified section.


Declaring the Interface Explicitly

In Listing 38.1, you saw how to implicitly implement an interface. This works fine for interfaces that do not conflict with each other, but what happens when you have two interfaces that specify the same method name and signature and should have different implementations? The answer is that you can't. You can't, that is, unless you explicitly implement the interfaces. To explicitly implement the interfaces, you simply omit the access modifier and then prepend the interface name to the method. For example, the following code snippet demonstrates how to explicitly implement the method DoSomething in the ISomeInterface interface:

class SomeClass : ISomeInterface
{
  void ISomeInterface.DoSomething()
  {
    ...
  }
}

TIP

When explicitly implementing an interface method, you must omit the access modifier. If you do not, the compiler will complain and your code will not run. Consequently, by omitting the access modifier, you will no longer be able to access that method from the class. To access this method, you have to gain access to an instance of the interface that was explicitly declared. In other words, you will have to cast the class to the desired interface and then you will be able to use the desired method.


Consider the following example. In real life, there are animals that walk erect and those that walk on all fours. If you were to create two interfaces, IWalkUpright and IWalkOnAllFours, and then declare a method in those interfaces called Walk, you would be able to apply IWalkUpright to humans and IWalkOnAllFours to other animals such as dogs. However, what happens when you try to model a bear? A bear can walk on all fours, but it can also (with enough motivation) walk upright. Therefore the bear would implement the IWalkUpright and IWalkOnAllFours interfaces. The problem is that both interfaces have the same method (Walk) declared inside of them and the implementation of Walk should be different for both of them as well. Listing 38.3 shows how explicitly declaring the interface methods solves this problem .

Listing 38.3. Two Interfaces with the Same Method and Signature
using System;

namespace TwoInterfacesSameMethod
{
  public interface IWalkOnAllFours 
  {
    void Walk();
  }
  public interface IWalkUpright
  {
    void Walk();
  }

  public class Bear : IWalkUpright, IWalkOnAllFours
  {
    #region IWalkUpright Members

    void IWalkUpright.Walk()
    {
      System.Console.WriteLine("Walking upright....");
    }

    #endregion

    #region IWalkOnAllFours Members

    void IWalkOnAllFours.Walk()
    {
      System.Console.WriteLine("Walking on all fours....");
    }

    #endregion
  }

  class TwoInterfacesSameMethod
  {
    public static void WalkUpright(IWalkUpright animal)
    {
      animal.Walk();
    }

    public static void WalkOnAllFours(IWalkOnAllFours animal)
    {
      animal.Walk();
    }

    [STAThread]
    static void Main(string[] args)
    {
      Bear bear = new Bear();
      WalkOnAllFours(bear);
      WalkUpright(bear);

      System.Console.ReadLine();
    }
  }
}

Mapping the Interface

Mapping the interface refers to the process of locating the interface that a method, property, indexer, or event belongs to. For simple classes that implement only one interface, this is relatively simple. For example, in the following code snippet, it is easy to tell that the Speak method in the class Dog is the implementation of the ISpeak interface:

public interface ISpeak
{
  void Speak();
}

public abstract class Dog : ISpeak
{
  #region ISpeak Members

  public void Speak()
  {
    ...
  }

  #endregion
}

Consider the example in Listing 38.4. In this listing, you have two classes and one interface. You also have class two inheriting from class1 and from the interface SomeInterface. Unless you know some basic interface mapping rules, it would be difficult to determine which methods would be called by looking at the code.

Fortunately, the basic rules are simple. First, explicit implementations take precedence. Next, the current class is searched for the implementation. If the implementation is not found there, the base classes are searched starting from left to right in the declaration.

Listing 38.4. Mapping the Interface
using System;

namespace MappingTheInterface
{
  interface ISomeInterface
  {
    void DoSomething();
  }

  class Class1 : ISomeInterface
  {
    #region ISomeInterface Members

    public void DoSomething()
    {
      System.Console.WriteLine("Doing something in Class 1.");
    }

    #endregion

  }

  class Class2 : Class1, ISomeInterface
  {
    #region ISomeInterface Members

    void ISomeInterface.DoSomething()
    {
      System.Console.WriteLine("Explicitly doing something.");
    }

    public new void DoSomething()
    {
      System.Console.WriteLine("Doing something in Class 2.");
    }

    #endregion
  }

  class MappingTheInterface
  {
    [STAThread]
    static void Main(string[] args)
    {
      Class2 c2 = new Class2();

      c2.DoSomething();
      ((ISomeInterface) c2).DoSomething();

      ((Class1) c2).DoSomething();

      System.Console.ReadLine();
    }
  }
}

If you know the basic rules, you can see that the code in Listing 38.4 will produce a result in the following manner (shown in Figure 38.6). First, because the class c2 explicitly implements the ISomeInterface interface and because you are accessing the method via a class reference, the Doing something in Class 2 message is printed. Next, because Class2 has an explicit implementation of the DoSomething method and you are casting the Class2 reference to an ISomeInterface interface, the Explicitly doing something message is printed. Finally, because you cast the Class2 reference to Class1, the Class1 class is searched first and the Doing something in Class 1 message is printed. The output of this listing is shown in Figure 38.6.

Figure 38.6. Output of Listing 38.4.


Inheriting the Interface

As previously mentioned in this chapter, interfaces can inherit from one or more base interfaces. When an interface inherits from another interface, it also inherits the contract. Therefore, if a class realizes an interface that inherits from another interface, it must implement all of the methods, properties, indexers, and events from both interfaces. For example, Listing 38.5 demonstrates an interface that inherits from another and a class that inherits from that interface.

Listing 38.5. Inheriting from Interfaces
using System;

namespace InterfaceInheritance
{
  public interface Interface1
  {
    void DoSomething1();
  }

  public interface Interface2 : Interface1
  {
    void DoSomething2();
  }

  class Class1 : Interface2
  {
    #region Interface2 Members

    public void DoSomething2()
    {
      System.Console.WriteLine("Doing something 2.");
    }

    #endregion

    #region Interface1 Members

    public void DoSomething1()
    {
      System.Console.WriteLine("Doing something 1.");
    }

    #endregion
  }


  class InterfaceInheritance
  {
    [STAThread]
    static void Main(string[] args)
    {
    }
  }
}

    Team LiB
    Previous Section Next Section