anon
anon

Reputation: 4618

Why doesn't this interface implementation work?

I seem to be having a basic syntax problem with an interface implementation. Basically I have this:

    public interface IMarkerInterface
    {       
    }

    public class ConcreteObject : IMarkerInterface
    {
    }

    public interface IDoStuffInterface
    {
        void DoStuff(IMarkerInterface obj);

        // also doesn't work
        // void DoStuff<T>(T obj) where T : IMarkerInterface;
    }

    public class ConcreteDoStuff : IDoStuffInterface
    {
        public void DoStuff(ConcreteObject c)
        {

        }
    }

To my mind, ConcreteObject implements IMarkerInterface, so therefore ConcreteDoStuff.DoStuff() should implement IDoStuffInterface.

But I get a compilation error "Error ConcreteDoStuff does not implement interface IDoStuffInterface.DoStuff()"

How come?

Upvotes: 2

Views: 215

Answers (6)

Daniel A.A. Pelsmaeker
Daniel A.A. Pelsmaeker

Reputation: 50316

If I give you any object implementing IDoStuffInterface, then you expect to be able to call a method DoStuff on it with as its argument any object that implements IMarkerInterface, right? However, if what you want would be possible, then you could somehow (invisible to you as user of the interface) only call DoStuff with a ConcreteObject, but not any other object implementing IMarkerInterface. Therefore, what you want is not really possible (nor desired, as it violates the Liskov Substitution Principle).

However, what you want can be done writing an explicit interface member implementation that implements the interface's DoStuff(IMarkerInterface) method, while providing a normal method DoStuff(ConcreteObject) implementation next to it. Any user of the interface will only see and be able to call the DoStuff(IMarkerInterface) method, but any user of ConcreteDoStuff will only directly be able to call the DoStuff(ConcreteObject) method. If the user wants to call the interface method anyway, the object must be cast to IDoStuffInterface first.

public class ConcreteDoStuff : IDoStuffInterface
{
    // Explicit interface member implementation:
    // This method is not directly visible as a member of the class.
    void IDoStuffInterface.DoStuff(IMarkerInterface obj)
    {
        // Do something with 'obj', or throw an exception when
        // it has the wrong type. Delegate the call to the
        // other DoStuff method if you wish.
    }

    // Normal method, technically not related to the interface method:
    public void DoStuff(ConcreteObject c)
    {
        // Do your thing.
    }
}

Edit:

Interesting workaround! Would you consider this to be common practice? Or is the workaround more of a "hack" to be avoided?

The technique of explicitly implementing an interface is well known, well understood and quite common. It is not considered to be a hack, and does not have to be avoided. It is even required in the rare case that a class implements two interfaces that both define a member with the same name, but you want to give them different behaviors.

However, in all cases where you limit the allowed input values or actions, you violate the Liskov Substitution Principle (LSP). In my example, you would limit the IMarkerInterface obj parameter to ConcreteObject objects. Other examples include: a Collection<T> that throws an exception when a null object is added; an IComparable.CompareTo implementation that throws an error when the argument has the wrong type; or a ReadOnlyCollection that throws an exception when calling its Add method.

While the LSP should not be violated (so the code becomes more (re)usable and testable), it is violated quite often, even in the .NET Framework itself. Not violating it in some cases may be cumbersome, result in hard-to-read code, or may even be impossible (e.g. due to restrictions of the classes and interfaces of the .NET Framework).

As I said, the same 'workaround' is applied to implementations of IComparable. For example, to implement a class that may only be compared to other objects of the same type:

public class BeanBag : IComparable, IComparable<BeanBag>
{
    private int beanCount;

    // Explicit interface member implementation:
    int IComparable.CompareTo(object other)
    {
        if (!(other is BeanBag))
            throw new ArgumentException("Wrong type!");
        // Calls the normal CompareTo() method.
        return CompareTo((BeanBag)other);
    }

    // Normal CompareTo method:
    public int CompareTo(BeanBag other)
    {
        if (other == null) return 1;
        return this.beanCount.CompareTo(other.beanCount);
    }
}

Upvotes: 4

Cameron Hotchkies
Cameron Hotchkies

Reputation: 1509

By only accepting ConcreteObject in your implementation of ConcreteDoStuff.DoStuff() you have limited the type of arguments that can be passed in.

If you were to create another class that also implemented the IMarkerInterface:

public class ConcreteObject2 : IMarkerInterface
{
}

It should be able to be passed in to any DoStuff() implementation, but ConcreteObject2 is not a ConcreteObject by definition, therefore the contract will bi violated, as the other answers have indicated.

Upvotes: 2

Garrett Vlieger
Garrett Vlieger

Reputation: 9494

You're best off using a generic interface:

public interface IMarkerInterface
{       
}

public class ConcreteObject : IMarkerInterface
{
}

public interface IDoStuffInterface<T>
{
    void DoStuff(T obj);
}

public class ConcreteDoStuff : IDoStuffInterface<ConcreteObject>
{
    public void DoStuff(ConcreteObject c)
    {

    }
}

Upvotes: 1

jtimperley
jtimperley

Reputation: 2544

Your implemented methods need to have the exact same signature as the interface. While all 'ConcreteObject' objects are of type 'IMarkerInterface', not all 'IMarkerInterfaces' will be 'ConcreteObject'. Thus the two signatures are not equivalent. Interfaces must be able to guarantee the CLR that any object of that type has a valid method implemented.

Upvotes: 5

Alexei Levenkov
Alexei Levenkov

Reputation: 100527

You need to implemet method with matching signature:

public class ConcreteDoStuff : IDoStuffInterface
{
    public void DoStuff(IMarkerInterface c) // not ConcreteObject c
    {

    }
}

Note you still can call ConcreteDoStuff.DoStuff with an instance of ConcreteObject :

 var concrete = new ConcreteObject()
 new ConcreteDoStuff().DoStuff(concrete);

You can also do this (explicit implementation of interface) if it works better for you:

public class ConcreteDoStuff : IDoStuffInterface
{
    public void DoStuff(ConcreteObject c)
    {

    }

    void IDoStuffInterface.DoStuff(IMarkerInterface c)
    {
       // some implementation preferably related to
       // DoStuff(ConcreteObject) i.e.: 
       DoStuff(c as ConcreteObject); 
    }
}

Upvotes: 2

Henk Holterman
Henk Holterman

Reputation: 273229

Your implementing class, ConcreteDoStuff, is trying to change the contract. Unilaterally.

Upvotes: 2

Related Questions