为什么.Net / C# 无法理解具有同名属性的接口继承?
考虑以下类和接口:
public interface A { string Property { get; set; } }
public interface B { string Property { get; set; } }
public interface C : A, B { }
public class MyClass : C
{
public string Property { get; set; }
}
看起来很简单,对吧?现在考虑以下程序:
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.Property = "Test";
A aTest = myClass;
B bTest = myClass;
C cTest = myClass;
aTest.Property = "aTest";
System.Console.WriteLine(aTest.Property);
bTest.Property = "bTest";
System.Console.WriteLine(bTest.Property);
cTest.Property = "cTest";
System.Console.WriteLine(cTest.Property);
System.Console.ReadKey();
}
看起来不错,但无法编译。它给了我一个歧义异常:
为什么 C# 无法弄清楚这一点?从建筑的角度来看,我所做的事情是否疯狂?我试图理解原因(我知道可以通过转换来解决)。
编辑
当我引入接口C
时出现了问题。当我使用 MyClass : A, B 时,我完全没有任何问题。
最终
刚刚完成了一个关于该主题的博客:接口歧义和隐式实现。
Consider the following class and interfaces:
public interface A { string Property { get; set; } }
public interface B { string Property { get; set; } }
public interface C : A, B { }
public class MyClass : C
{
public string Property { get; set; }
}
Looks simple, right? Now consider the following program:
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.Property = "Test";
A aTest = myClass;
B bTest = myClass;
C cTest = myClass;
aTest.Property = "aTest";
System.Console.WriteLine(aTest.Property);
bTest.Property = "bTest";
System.Console.WriteLine(bTest.Property);
cTest.Property = "cTest";
System.Console.WriteLine(cTest.Property);
System.Console.ReadKey();
}
Looks okay, but it will not compile. It gives me an Ambiguity exception:
Why isn't C# able to figure this out? Is what I'm doing crazy from an architectural point of view? I'm trying to understand the why (I know it can be solved with casting).
EDIT
The problems arose when I introduced interface C
. When I use MyClass : A, B
I've got no problems at all.
FINAL
Just finised a blog about the subject: Interface Ambiguity and Implicit Implementation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
简而言之,因为它确实含糊不清。
现在更详细的故事。正如您已经看到的,存在显式接口实现,因此您可以对 A.Property 和 B.Property 有两种不同的实现,而当您只有 C 时,您无法判断实现是否相同。由于C#的“哲学”不是猜测你的意思,而是让你在必要时说得更清楚,所以编译器不会选择A.Property或B.Property,而是报告错误。
In short because it's ambiguous indeed.
Now more detailed story. As you've already seen there is explicit interface implementation, so you can have two different implementations for A.Property and B.Property and when you have only C there is no way you can tell if implementations are the same or not. Since C# "philosophy" is not to guess what you meant, but make you state it more clear when necessary, compiler does not choose either A.Property or B.Property, but reports an error.
您需要显式接口实现
:是时候调用它们了,你必须这样做:
为什么不这样做:
应该注意的是,这是糟糕的设计,如果你要公开接口 C,请确保找到更好的公开方式A/B. 属性。
You need explicit interface implementation:
When it comes time to call them you are going to have to do:
Why don't you do:
And it should be noted this is bad design, if you are going to expose an interface C, make sure you find a better way to expose A/B.Property.
需要弄清楚什么? cTest是“C”类型,它从两个不同的类继承“Property”;编译器不知道你想要哪一个。这种行为是从 C++ 继承的;这是“为什么多重继承是潘多拉魔盒”的经典例子。
其他面向对象的语言(Java 是一个著名的例子)通过定义避免了这个问题:相似命名/相似签名的方法被融合在一个共同的后代中。
What's to figure out? cTest is of type "C", and it inherits "Property" from two different classes; the compiler doesn't know which one you want. This sort of behavior is inherited from C++; it's the classic example of "why multiple inheritance is a Pandora's box."
Other object-oriented languages -- Java is a notable example -- avoid this problem by definition : like-named/like-signatured methods are fused in a common descendent.
当您从单个接口继承时,编译器可以在您添加新方法时准确确定您有兴趣实现哪个方法。
然而,当多个接口具有相同的方法时,潜在的(且正确的)假设是每个接口期望该方法有不同的实现,因为这些方法或属性是在不同的接口上定义的。
因此编译器告诉您这些不同的接口需要对每个属性进行显式实现。
两个接口共享属性或方法的相同名称这一事实是任意的 - 没有理由假设它们共享除名称之外的任何其他内容,因此编译器可以防止您犯以相同方式隐式处理它们的错误。
When you inherit from a single interface the compiler can determine exactly which method you are interested in implementing when you add the new method.
However when multiple interfaces have the same method, the underlying (and correct) assumption is that each interface expects a DIFFERENT implementation for the method, due to the fact that those methods or properties are defined on different interfaces.
So the compiler tells you that these different interfaces require an explicit implementation for each of these properties.
The fact that two interfaces share the same NAME for a property or method is arbitrary - there is no reason to assume that they share anything OTHER then the name, so the compiler protects you from making the mistake of implicitly treating them in the same way.
这并不简单,看起来也不简单。如果两个接口之间发生名称冲突,.NET 需要询问您要尝试实现哪个接口。它问你这个问题的方式是通过歧义错误。
如果您没有遇到此类错误,那么您最终会偶然实现接口。
It is not simple, and it doesn't look simple either. In case of a name collision between two interfaces, .NET needs to ask you which interface are you trying to implement. Its way to ask you this is via the ambiguity error.
If you didn't have this kind of errors, you would end up implementing interfaces by chance.
您需要明确实现这两个属性从每个界面:
you need to explicity implement both properties from each interface:
因为你所做的事情是不对的。 A 和 B 发生冲突并且属性名称相同...您需要使用接口的显式实现。
请参考此处。
Because what you are doing is not right. A and B are clashing and have the same name for the property... you need to use Explicit implementation of interface.
Reference here.
有很多答案,而且所有答案都是正确的,因为显式接口实现就是您问题的答案。
我将尝试用一个有点复杂的示例来阐明此设计背后的动机:
假设我有一个供跑步者使用的界面(可能的实现如
LongDistanceRunner
、Jogger
、MarathonMan
等)以及可打开和运行的设备界面(可能实现
BathTub
、Application
、Dishwasher< /code> 等)
现在我想为
IMusicalJogger
创建接口(例如JoggerWithIpod
、BoomBoxJogger
等实现)现在,当我说
bbJogger.Run()
我的对象应该做什么?它应该开始跑过公园,还是应该打开扬声器,或者两者都打开,或者完全是别的什么?如果我同时实现类和调用点,很明显我希望我的慢跑者同时执行这两个操作,但是如果我只控制调用点怎么办?如果该接口有其他实现来做其他事情怎么办?如果我的慢跑者开始穿过公园,当它在被视为设备(通过铸造)的环境中使用时会怎么样。这就是显式接口实现发挥作用的地方。
我必须这样定义我的类:
然后,当我打电话时,我必须指定我想要使用慢跑机的哪个方面:
希望我能帮助澄清这个概念。
There are a lot of answers, and all of them are right, as explicit interface implementation is the answer to your problem.
I'll try to clarify the motivation behind this design with a somewhat convoluted example:
Let's say I have an interface for people that run (with possible implementations like
LongDistanceRunner
,Jogger
,MarathonMan
, etc)and an interface for devices that can be turned on and ran (with possible implementations
BathTub
,Application
,Dishwasher
, etc)Now I want to create and interface for a
IMusicallJogger
(implementations likeJoggerWithIpod
,BoomBoxJogger
, etc)Now, when I say
bbJogger.Run()
what should my object do? Should it start running across the park, or should it turn on the boombox, or both, or something else entirely? If I implement both the class and the callsite, it might be obvious that I want my joggers to do both, but what if I control just the callsite? And what if there are other implementations of the interface that do something else? And what if my jogger starts running across the park, when it's used in a context where it is considered like a device (through casting).That's where explicit interface implementation comes into play.
I have to define my class like this:
and then, when I call, I have to specify what aspect of my jogger I want to use:
Hope I helped clarify the concept.