为什么.Net / C# 无法理解具有同名属性的接口继承?

发布于 2024-10-30 14:17:46 字数 1329 浏览 3 评论 0原文

考虑以下类和接口:

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

看起来不错,但无法编译。它给了我一个歧义异常:

Screenshot compiler

为什么 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:

Screenshot compiler

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

一世旳自豪 2024-11-06 14:17:46

简而言之,因为它确实含糊不清。

现在更详细的故事。正如您已经看到的,存在显式接口实现,因此您可以对 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.

故事灯 2024-11-06 14:17:46

您需要显式接口实现

public interface A { string Property { get; set; } }

public interface B { string Property { get; set; } }

public interface C : A, B { }

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
}

:是时候调用它们了,你必须这样做:

MyClass c = new MyClass();
Console.WriteLine("Property A is ": ((A)c).Property);

为什么不这样做:

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
    string B { get { return B.Property; } set { B.Property=value; } }
    string A { get { return A.Property; } set { A.Property=value; } }

}

应该注意的是,这是糟糕的设计,如果你要公开接口 C,请确保找到更好的公开方式A/B. 属性。

You need explicit interface implementation:

public interface A { string Property { get; set; } }

public interface B { string Property { get; set; } }

public interface C : A, B { }

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
}

When it comes time to call them you are going to have to do:

MyClass c = new MyClass();
Console.WriteLine("Property A is ": ((A)c).Property);

Why don't you do:

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
    string B { get { return B.Property; } set { B.Property=value; } }
    string A { get { return A.Property; } set { A.Property=value; } }

}

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.

是你 2024-11-06 14:17:46

需要弄清楚什么? 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.

鲜血染红嫁衣 2024-11-06 14:17:46

当您从单个接口继承时,编译器可以在您添加新方法时准确确定您有兴趣实现哪个方法。

然而,当多个接口具有相同的方法时,潜在的(且正确的)假设是每个接口期望该方法有不同的实现,因为这些方法或属性是在不同的接口上定义的。

因此编译器告诉您这些不同的接口需要对每个属性进行显式实现。

两个接口共享属性或方法的相同名称这一事实是任意的 - 没有理由假设它们共享除名称之外的任何其他内容,因此编译器可以防止您犯以相同方式隐式处理它们的错误。

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.

岁月静好 2024-11-06 14:17:46

这并不简单,看起来也不简单。如果两个接口之间发生名称冲突,.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.

坦然微笑 2024-11-06 14:17:46

您需要明确实现这两个属性从每个界面:

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 

you need to explicity implement both properties from each interface:

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 
自演自醉 2024-11-06 14:17:46

因为你所做的事情是不对的。 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.

淡墨 2024-11-06 14:17:46

有很多答案,而且所有答案都是正确的,因为显式接口实现就是您问题的答案。

我将尝试用一个有点复杂的示例来阐明此设计背后的动机:

假设我有一个供跑步者使用的界面(可能的实现如 LongDistanceRunnerJoggerMarathonMan 等)

public interface IRunner 
{
   void Run();
}

以及可打开和运行的设备界面(可能实现 BathTubApplicationDishwasher< /code> 等)

public interface IRunnable
{
   void Run();
}

现在我想为 IMusicalJogger 创建接口(例如 JoggerWithIpodBoomBoxJogger 等实现)

public interface IMusicalJogger : IRunner, IRunnable {}

public class BoomBoxJogger : IMusicalJogger
{
   // code here
}

BoomBoxJogger bbJogger = new BoomBoxJogger();

现在,当我说 bbJogger.Run() 我的对象应该做什么?它应该开始跑过公园,还是应该打开扬声器,或者两者都打开,或者完全是别的什么?如果我同时实现类和调用点,很明显我希望我的慢跑者同时执行这两个操作,但是如果我只控制调用点怎么办?如果该接口有其他实现来做其他事情怎么办?如果我的慢跑者开始穿​​过公园,当它在被视为设备(通过铸造)的环境中使用时会怎么样。

这就是显式接口实现发挥作用的地方。

我必须这样定义我的类:

public class BoomBoxJogger : IMusicalJogger
{
   void IRunner.Run() //implementation of the runner aspect
   {
      Console.WriteLine("Running through the park");
   }

   void IRunnable.Run() //implementation of the runnable aspect
   {
      Console.WriteLine("Blasting out Megadeth on my boombox");
   }

   public void Run() //a new method defined in the class itself 
   {
      Console.WriteLine("Running while listening to music");
   }

}

然后,当我打电话时,我必须指定我想要使用慢跑机的哪个方面:

BoomBoxJogger bbJogger = new BoomBoxJogger();
((IRunner).bbJogger).Run(); // start running
((IRunnable).bbJogger).Run(); // blast the boombox
//and of course you can now do
bbJogger.Run //running while listening

((IMusicalJogger)jogger).Run(); //compiler error here, as there is no way to resolve this.

希望我能帮助澄清这个概念。

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)

public interface IRunner 
{
   void Run();
}

and an interface for devices that can be turned on and ran (with possible implementations BathTub, Application, Dishwasher, etc)

public interface IRunnable
{
   void Run();
}

Now I want to create and interface for a IMusicallJogger (implementations like JoggerWithIpod,BoomBoxJogger, etc)

public interface IMusicalJogger : IRunner, IRunnable {}

public class BoomBoxJogger : IMusicalJogger
{
   // code here
}

BoomBoxJogger bbJogger = new BoomBoxJogger();

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:

public class BoomBoxJogger : IMusicalJogger
{
   void IRunner.Run() //implementation of the runner aspect
   {
      Console.WriteLine("Running through the park");
   }

   void IRunnable.Run() //implementation of the runnable aspect
   {
      Console.WriteLine("Blasting out Megadeth on my boombox");
   }

   public void Run() //a new method defined in the class itself 
   {
      Console.WriteLine("Running while listening to music");
   }

}

and then, when I call, I have to specify what aspect of my jogger I want to use:

BoomBoxJogger bbJogger = new BoomBoxJogger();
((IRunner).bbJogger).Run(); // start running
((IRunnable).bbJogger).Run(); // blast the boombox
//and of course you can now do
bbJogger.Run //running while listening

((IMusicalJogger)jogger).Run(); //compiler error here, as there is no way to resolve this.

Hope I helped clarify the concept.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文