为什么这个接口实现不起作用?

时间:2012-08-14 22:19:43

标签: c# inheritance interface

我似乎遇到了接口实现的基本语法问题。基本上我有这个:

    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)
        {

        }
    }

在我看来,ConcreteObject实现了IMarkerInterface,因此ConcreteDoStuff.DoStuff()应该实现IDoStuffInterface

但是我收到了编译错误"Error ConcreteDoStuff does not implement interface IDoStuffInterface.DoStuff()"

怎么回事?

6 个答案:

答案 0 :(得分:5)

您实现的方法需要与接口具有完全相同的签名。虽然所有'ConcreteObject'对象都是'IMarkerInterface'类型,但并非所有'IMarkerInterfaces'都是'ConcreteObject'。因此,这两个签名并不相同。接口必须能够保证CLR能够实现该类型的任何对象。

答案 1 :(得分:4)

如果我给你任何实现IDoStuffInterface的对象,那么你希望能够在其上调用一个方法DoStuff作为其参数任何实现{{em>的对象 1}},对吗?但是,如果您想要的是什么,那么您可以以某种方式(对于您作为界面的用户而言不可见)仅使用IMarkerInterface调用DoStuff,但实现ConcreteObject的任何其他对象。因此,你想要的东西实际上是不可能的(也不是理想的,因为它违反了Liskov Substitution Principle)。

但是,您可以编写实现接口IMarkerInterface方法的explicit interface member implementation,同时在其旁边提供常规方法DoStuff(IMarkerInterface)。该界面的任何用户都只能看到并能够调用DoStuff(ConcreteObject)方法,但DoStuff(IMarkerInterface)的任何用户都只能直接调用ConcreteDoStuff方法。如果用户想要调用接口方法,则必须首先将对象 强制转换为DoStuff(ConcreteObject)

IDoStuffInterface

编辑:

有趣的解决方法!你认为这是常见做法吗?或者解决方法更多的是要避免的“黑客”?

显式实现接口的技术是众所周知的,很好理解并且很常见。它不被认为是 hack ,并且不必避免。在极少数情况下甚至需要一个类实现两个接口,这两个接口都定义了一个具有相同名称的成员,但是你想给它们不同的行为。

但是,在限制允许的输入值或操作的所有情况下,都违反了Liskov替换原则(LSP)。在我的示例中,您将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. } } 参数限制为IMarkerInterface obj个对象。其他示例包括:Collection<T>在添加ConcreteObject对象时抛出异常;一个IComparable.CompareTo实现,当参数的类型错误时会抛出错误;或者在调用null方法时抛出异常的ReadOnlyCollection

虽然不应该违反LSP(因此代码变得更加可用和可测试),但 经常违反,即使在.NET Framework本身也是如此。在某些情况下不违反它可能很麻烦,导致难以阅读的代码,甚至可能不可能(例如由于.NET Framework的类和接口的限制)。

正如我所说,同样的“解决方法”适用于Add的实现。例如,要实现一个只能与相同类型的其他对象进行比较的类:

IComparable

答案 2 :(得分:2)

您的实施班ConcreteDoStuff正在尝试更改合同。单方面。

答案 3 :(得分:2)

您需要使用匹配签名实现方法:

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

    }
}

请注意,您仍然可以使用ConcreteObject的实例调用ConcreteDoStuff.DoStuff:

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

如果它对您更有效,您也可以这样做(显式实现界面):

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); 
    }
}

答案 4 :(得分:2)

只在ConcreteObject的实现中接受ConcreteDoStuff.DoStuff(),你限制了可以传入的参数类型。

如果您要创建另一个同时实现IMarkerInterface

的类
public class ConcreteObject2 : IMarkerInterface
{
}

它应该能够传递到任何DoStuff()实现,但ConcreteObject2根据定义不是ConcreteObject,因此合同将违反,因为其他答案已表明

答案 5 :(得分:1)

您最好使用通用界面:

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)
    {

    }
}