为什么.Net / C#不能理解与同名属性的接口继承?

时间:2011-04-05 11:55:36

标签: c# .net inheritance interface ambiguity

考虑以下类和接口:

    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时,我根本没有任何问题。

FINAL

刚刚完成了关于这个主题的博客:Interface Ambiguity and Implicit Implementation

8 个答案:

答案 0 :(得分:7)

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

现在更详细的故事。正如您已经看到的那样,有明确的接口实现,因此您可以为A.Property和B.Property提供两种不同的实现,当您只有C时,您无法判断实现是否相同。由于C#“哲学”不是猜测你的意思,而是让你在必要时更清楚地说明,编译器既不选择A.Property也不选择B.Property,但报告错误。

答案 1 :(得分:4)

您需要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; }
}

到了打电话给他们的时候,你将不得不这样做:

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.Property。

答案 2 :(得分:3)

要弄清楚什么? cTest的类型为“C”,它从两个不同的类继承“Property”;编译器不知道你想要哪一个。这种行为继承自C ++;这是“为什么多重继承是潘多拉盒子的经典例子”。

其他面向对象语言 - Java是一个值得注意的例子 - 根据定义避免这个问题:like-named / like-signatured方法融合在一个共同的后代中。

答案 3 :(得分:3)

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

然而,当多个接口具有相同的方法时,基础(和正确)假设是每个接口都需要该方法的DIFFERENT实现,因为这些方法或属性是在不同的接口上定义的。

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

两个接口共享属性或方法的相同NAME这一事实是任意的 - 没有理由认为它们共享除名称之外的任何东西,因此编译器可以保护您不会错误地将其隐藏在同样的方式。

答案 4 :(得分:2)

这不简单,看起来也不简单。如果两个接口之间发生名称冲突,.NET需要询问您尝试实现哪个接口。问你的方法是通过歧义错误。

如果你没有这种错误,你最终会偶然实现接口。

答案 5 :(得分:2)

您需要explicity implement来自每个界面的两个属性:

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

答案 6 :(得分:0)

因为你在做什么是不对的。 A和B发生冲突并且属性名称相同......您需要使用显式接口的实现。

参考here

答案 7 :(得分:0)

有很多答案,而且所有答案都是正确的,因为明确的界面实现是您问题的答案。

我将尝试用一个有些复杂的例子来澄清这种设计背后的动机:

假设我有一个运行人员的界面(可能有LongDistanceRunnerJoggerMarathonMan等实现

public interface IRunner 
{
   void Run();
}

以及可以打开和运行的设备界面(可能的实施BathTubApplicationDishwasher等)

public interface IRunnable
{
   void Run();
}

现在我想为IMusicallJogger创建和界面(JoggerWithIpodBoomBoxJogger等实现)

public interface IMusicalJogger : IRunner, IRunnable {}

public class BoomBoxJogger : IMusicalJogger
{
   // code here
}

BoomBoxJogger bbJogger = new BoomBoxJogger();

现在,当我说bbJogger.Run()我的对象应该做什么?它应该开始穿过公园,还是应该打开音箱,或两者,或其他完全?如果我同时实现类和调用点,很明显 I 希望我的慢跑者同时做这两件事,但是如果我只控制调用点呢?如果接口的其他实现做了其他什么呢?如果我的慢跑者开始穿​​过公园,当它被用在一个被认为是装置的环境中时(通过铸造)会怎么样。

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

我必须像这样定义我的课程:

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.

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