我似乎遇到了接口实现的基本语法问题。基本上我有这个:
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()"
怎么回事?
答案 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)
{
}
}