问题描述
在下面的示例类中,"SomeClass"未实现"ISomeInterface".为什么我不能通过传递确实实现基本要求的派生接口来实现此目的.无论通过什么实例,它仍然会实现基础,我是否缺少某些东西?
namespace Test
{
public interface IBaseInterface
{
void DoBaseStuff();
}
public interface IChildInterface : IBaseInterface
{
void DoChildStuff();
}
public interface ISomeInterface
{
void DoSomething(IBaseInterface baseInterface);
}
public class SomeClass : ISomeInterface
{
public void DoSomething(IChildInterface baseInterface)
{
}
}
}
之所以存在此限制,是因为ISomeInterface
期望任何IBaseInterface
都将满足合同要求.也就是说,如果您具有以下条件:
public interface IBase {}
public interface IChildA : IBase {}
public interface IChildB : IBase {}
还有一个期望IBase
的接口:
public interface IFoo { void Bar(IBase val); }
然后根据需要将其限制在派生类中:
public class Foo : IFoo { public void Bar(IChildA val) {} }
会产生以下问题:
IChildB something = new ChildB();
IFoo something = new Foo();
something.Bar(something); // This is an invalid call
正因为如此,您并未执行您原本打算执行的合同.
在这种情况下,您有两个简单选项:
-
将
IFoo
调整为通用,并接受作为IBase
派生的T
:public interface IFoo<T> where T : IBase { void Bar(T val); } public class Foo : IFoo<IChildA> { public void Bar(IChildA val) {} }
当然,这意味着
Foo
不能再接受任何IBase
(包括IChildB
). -
调整
Foo
以实现IFoo
,并为void Bar(IChildA val)
使用附加的实用程序方法:public class Foo : IFoo { public void Bar(IBase val) {} public void Bar(IChildA val) {} }
这有一个有趣的副作用:每当您调用
((IFoo)foo).Bar
时,它将期望IBase
;当您调用foo.Bar
时,它将期望IChildA
或IBase
.这意味着它可以满足合同要求,同时还具有特定于派生接口的方法.如果您想更多地隐藏"Bar(IBase)
方法,则可以显式实现IFoo
:void IFoo.Bar(IBase val) { }
这会在您的代码中造成更多不一致的行为,因为现在
((IFoo)foo).Bar
与foo.Bar
完全不同 ,但我将决定权留给您. /p>这意味着,对于本节的第二个版本,
foo.Bar(new ChildB());
现在无效,因为IChildB
是 not 和IChildA
.
由于我上面提到的原因,这是不允许的,IFoo.Bar
期望任何 IBase
,而您想进一步将类型限制为IChildA
,它不是IBase
的超级接口,即使是IBase
的超级接口,也可以使用,因为它违反了接口的实现,尽管您可以更容易地进行定义,此时第二种方法可以满足您的需求.
请记住,当您实现interface
时,您订阅了合同,而C#将不允许您违反该合同.
In the following sample class "SomeClass" does not implement "ISomeInterface". Why can't I implement this by passing a more derived interface which does implement the base requirement. Whatever instance would be passed it would still implement the base, am I missing something?
namespace Test
{
public interface IBaseInterface
{
void DoBaseStuff();
}
public interface IChildInterface : IBaseInterface
{
void DoChildStuff();
}
public interface ISomeInterface
{
void DoSomething(IBaseInterface baseInterface);
}
public class SomeClass : ISomeInterface
{
public void DoSomething(IChildInterface baseInterface)
{
}
}
}
This restriction exists because the ISomeInterface
expects that any IBaseInterface
will satisfy the contract. That is, if you have the following:
public interface IBase {}
public interface IChildA : IBase {}
public interface IChildB : IBase {}
And an interface that expects IBase
:
public interface IFoo { void Bar(IBase val); }
Then restricting this in a derived class as you would like:
public class Foo : IFoo { public void Bar(IChildA val) {} }
Would create the following problem:
IChildB something = new ChildB();
IFoo something = new Foo();
something.Bar(something); // This is an invalid call
As such, you're not implementing the contract you said you would.
In this situation, you have two simple options:
Adjust
IFoo
to be generic, and accept aT
that is a derivation ofIBase
:public interface IFoo<T> where T : IBase { void Bar(T val); } public class Foo : IFoo<IChildA> { public void Bar(IChildA val) {} }
Of course, this means that
Foo
can no longer accept anyIBase
(includingIChildB
).Adjust
Foo
to implementIFoo
, with an additional utility method forvoid Bar(IChildA val)
:public class Foo : IFoo { public void Bar(IBase val) {} public void Bar(IChildA val) {} }
This has an interesting side-effect: whenever you call
((IFoo)foo).Bar
it will expectIBase
, and when you callfoo.Bar
it will expectIChildA
orIBase
. This means it satisfies the contract, while also having your derived-interface-specific method. If you want to "hide" theBar(IBase)
method more, you could implementIFoo
explicitly:void IFoo.Bar(IBase val) { }
This creates even more inconsistent behavior in your code, as now
((IFoo)foo).Bar
is completely different fromfoo.Bar
, but I leave the decision up to you.This means, with the second version in this section, that
foo.Bar(new ChildB());
is now invalid, asIChildB
is not anIChildA
.
This is not allowed because of the reasoning I mentioned above, IFoo.Bar
expects any IBase
, whereas you want to further constrain the type to IChildA
, which is not a super-interface of IBase
, and even if it were it would not be allowed because it violates the interface implementation, though you could more easily define a second method at that point that does what you want.
Keep in mind that when you implement an interface
, you subscribe to a contract, and C# will not let you violate that contract.
这篇关于具有派生接口的C#接口实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!