O'Reilly logo

Programming C# 4.0 by Jesse Liberty, Matthew Adams, Ian Griffiths

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Deriving Interfaces from Other Interfaces

Interfaces support inheritance too, just like classes. If you want, you could create a named, salaried person interface like this:

interface INamedSalariedPerson : INamedPerson, ISalariedPerson
{
}

What happens if you have conflicting names? Imagine the interface ISettableNamedPerson:

interface ISettableNamedPerson
{
    string Name
    {
        get; set;
    }
}

What happens if we implement both INamedPerson and ISettableNamedPerson on our FirefighterBase?

abstract class FirefighterBase : INamedPerson, ISettableNamedPerson, ISalariedPerson
{
    // ...
}

The answer is that everything is just fine! Each interface requires that we implement a string property called Name; one requires at least a getter, the other a getter and a setter.

When we access the property through the relevant interface, it can resolve correctly which member we meant; there’s no requirement for a separate implementation for each interface.

But what if that was actually wrong? What if our Name property on INamedPerson had entirely different semantics from the one on ISettableNamedPerson? Let’s suppose that one is intended to allow only letters and numbers with no spaces and the other is just our freeform “any old text” implementation with which we are familiar.

Whenever our client expects an INamedPerson we need to provide the second implementation, and whenever the client expects an ISettableNamedPerson, the first.

We can do that by explicitly implementing the interfaces.

Explicit Interface Implementation

To explicitly implement a particular member of an interface, you drop the accessibility modifier and add the interface name as a prefix, as shown in Example 4-24.

Example 4-24. Explicit interface implementation

class AFootInBothCamps : INamedPerson, ISettableNamedPerson
{
    private string settableName;

    string INamedPerson.Name
    {
        get
        {
            Console.WriteLine("Accessed through the INamedPerson interface");
            return settableName;
        }
    }

    string ISettableNamedPerson.Name
    {
        get
        {
            return settableName;
        }
        set
        {
            Console.WriteLine(
            "Accessed through the " +
            "ISettableNamedPerson interface");

            if( settableName != null && settableName.Contains(" ") )
            {
                // You can't set it if it contains the space
                // character
                return;
            }
            settableName = value;
        }
    }
}

Example 4-25 shows how we’re going to access them from our main function.

Example 4-25. Calling different interface implementations of the same member name on the same object

class Program
{
    static void Main(string[] args)
    {
        AFootInBothCamps both = new AFootInBothCamps();

        ISettableNamedPerson settablePerson = both;
        INamedPerson namedPerson = both;

        settablePerson.Name = "hello";

        Console.WriteLine(settablePerson.Name);
        Console.WriteLine(namedPerson.Name);

        Console.ReadKey();
    }
}

Notice how we’re creating our object, and then providing two additional references to it: one through a variable of type ISettableNamedPerson and one through INamedPerson.

We then call on the Name property through each of those interfaces, and get the following output:

Accessed through the ISettableNamedPerson interface
hello
Accessed through the INamedPerson interface
hello

But what if we try to access it through a reference typed to the class itself?

Console.WriteLine(both.Name);

Add the following line to the main function and compile, and we get a compiler error!

'AFootInBothCamps' does not contain a definition for 'Name' and no extension
 method 'Name' accepting a first argument of type 'AFootInBothCamps' could be
 found (are you missing a using directive or an assembly reference?)

We’ve seen that error before; it means we’re trying to talk to a member that doesn’t exist. What’s happened is that the members that are explicitly implemented exist only if we are accessing them through the relevant interfaces.

However, as long as we explicitly implement one of the two (or two of the three, or however many we’re stuck with), we can choose one interface as our “default” and implement it using the regular syntax, as shown in Example 4-26.

Example 4-26. Implementing one of the interfaces implicitly

class AFootInBothCamps : INamedPerson, ISettableNamedPerson
{
    private string settableName;

    // Regular implementation syntax
    public string Name
    {
        get
        {
            Console.WriteLine("Accessed through the INamedPerson interface");
            return settableName;
        }
    }

    string ISettableNamedPerson.Name
    {
        get
        {
            return settableName;
        }
        set
        {
            Console.WriteLine("Accessed through the ISettableNamedPerson "
                              + "interface");
            if( settableName != null && settableName.Contains(" ") )
            {
                // You can't set it if it contains the space
                // character
                return;
            }
            settableName = value;
        }
    }
}

Now we can compile and run, and the default implementation for our class is the one for the INamedPerson interface:

Accessed through the ISettableNamedPerson interface
hello
Accessed through the INamedPerson interface
hello
Accessed through the INamedPerson interface
hello

Note

In real life, you don’t often come across this need for explicit interface implementation. If you have control over all the code in the application, you should avoid designing in a clash where the names are the same but the semantics are different. Like overloads or overrides with different meanings, it surprises other developers.

The .NET Framework contains a few examples where it uses explicit interface implementation to hide the interface members from the public API of a class, even though there is no clash. The authors are unconvinced that this improves matters.

More often, you will come across this usage where you don’t have control of the code—with two third-party libraries, for instance, both of which declare interfaces with different semantics but a clash of names. Even then, this is not a problem unless you happen to need to implement both interfaces on one class. Even rarer!

Right, let’s go back to our FireStation class for a minute, and imagine an interface we could create to formalize the contract for clocking in: our billing system might define this contract for us so that we can plug into it.

As it happens, our FireStation provides an implementation which can ClockIn named people, but our billing system’s IClockIn contract is much more generic—it can clock in anything of type Object, as we had in our original implementation:

interface IClockIn
{
    void ClockIn(object item);
}

We can now implement IClockIn on our FireStation, as shown in Example 4-27.

Example 4-27. Implementing the IClockIn interface

class FireStation : IClockIn
{
    List<INamedPerson> clockedInStaff = new List<INamedPerson>();

    public void ClockIn(INamedPerson staffMember)
    {
        if (!clockedInStaff.Contains(staffMember))
        {
            clockedInStaff.Add(staffMember);
            Console.WriteLine("Clocked in {0}", staffMember.Name);
        }
    }

    public void RollCall()
    {
        foreach (INamedPerson staffMember in clockedInStaff)
        {
            Console.WriteLine(staffMember.Name);
        }
    }

    public void ClockIn(object item)
    {
        // What to do here
    }

}

Our original ClockIn method is unchanged, and we’ve added a new overload that takes an object, and therefore matches the requirement in our interface. But how do we implement that new method? We want to check that the person being clocked in is an INamedPerson, and if it is, perform our usual operation. Otherwise, we want to tell the user that we can’t clock him in.

In other words, we need a manual check for the type of the object.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required