7

考虑以下类和接口:

    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# 不能解决这个问题?从架构的角度来看,我正在做的事情是不是很疯狂?我试图了解原因(我知道可以通过强制转换来解决)。

编辑

当我介绍 interface 时出现了问题C。当我使用时MyClass : A, B,我完全没有问题。

最终的

刚刚完成了一个关于这个主题的博客:接口歧义和隐式实现

4

8 回答 8

8

简而言之,因为它确实模棱两可。

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

于 2011-04-05T12:08:57.637 回答
4

您需要显式接口实现

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。

于 2011-04-05T11:58:47.593 回答
3

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

其他面向对象的语言——Java 是一个显着的例子——通过定义避免了这个问题:类似命名/类似签名的方法被融合在一个共同的后代中。

于 2011-04-05T11:59:45.420 回答
3

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

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

所以编译器会告诉你,这些不同的接口需要对这些属性中的每一个进行显式实现。

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

于 2011-04-05T12:09:07.480 回答
2

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

如果你没有出现这种错误,你最终会碰巧实现接口。

于 2011-04-05T11:57:59.990 回答
2

您需要从每个接口显式地实现这两个属性:

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 
于 2011-04-05T11:59:08.270 回答
0

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

参考这里

于 2011-04-05T11:59:56.007 回答
0

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

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

假设我有一个供人们运行的接口(可能的实现,如LongDistanceRunner, Jogger,MarathonMan等)

public interface IRunner 
{
   void Run();
}

以及可以打开和运行的设备接口(可能的实现BathTub、、、ApplicationDishwasher

public interface IRunnable
{
   void Run();
}

现在我想为 a 创建和接口IMusicallJogger(例如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.

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

于 2011-04-05T12:33:55.173 回答