Почему .Net / C # не может понять наследование интерфейса со свойствами с одинаковыми именами? - PullRequest
6 голосов
/ 05 апреля 2011

Рассмотрим следующий класс и интерфейсы:

    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 # не может понять это? Это то, что я делаю сумасшедшим с архитектурной точки зрения? Я пытаюсь понять , почему (я знаю, что это можно решить с помощью литья).

EDIT

Проблемы возникли, когда я ввел интерфейс C. Когда я использую MyClass : A, B, у меня вообще нет проблем.

FINAL

Только что закончил блог на эту тему: Неоднозначность интерфейса и неявная реализация .

Ответы [ 8 ]

7 голосов
/ 05 апреля 2011

Короче, потому что это действительно неоднозначно.

Теперь более подробный рассказ.Как вы уже видели, существует явная реализация интерфейса, поэтому вы можете иметь две разные реализации для A.Property и B.Property, и когда у вас есть только C, вы не сможете определить, являются ли реализации одинаковыми или нет.Так как «философия» C # состоит не в том, чтобы угадать, что вы имели в виду, а в том, чтобы сделать это более понятным, когда это необходимо, компилятор не выбирает ни A.Property, ни B.Property, но сообщает об ошибке.

4 голосов
/ 05 апреля 2011

Вам нужно явная реализация интерфейса :

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.

3 голосов
/ 05 апреля 2011

Когда вы наследуете от одного интерфейса, компилятор может точно определить, какой метод вы заинтересованы реализовать при добавлении нового метода.

Однако, когда несколько интерфейсов имеют один и тот же метод, базовый (и правильный)Предполагается, что каждый интерфейс ожидает РАЗЛИЧНУЮ реализацию для метода из-за того, что эти методы или свойства определены на разных интерфейсах.

Таким образом, компилятор говорит вам, что эти разные интерфейсы требуют явной реализации для каждого изэти свойства.

Тот факт, что два интерфейса совместно используют одно и то же NAME для свойства или метода, является произвольным - нет оснований предполагать, что они совместно используют что-либо ДРУГОЕ, чем имя, поэтому компилятор защищает вас от ошибкинеявно относиться к ним таким же образом.

3 голосов
/ 05 апреля 2011

Что выяснить? cTest имеет тип "C" и наследует "Property" от двух разных классов; компилятор не знает, какой вы хотите. Такое поведение наследуется от C ++; это классический пример «почему множественное наследование - это ящик Пандоры».

Другие объектно-ориентированные языки - яркий пример - Java - избегайте этой проблемы по определению: методы like-named / like-signatured объединены в общем потомке.

2 голосов
/ 05 апреля 2011

вам нужно Explicity реализовать оба свойства из каждого интерфейса:

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 
2 голосов
/ 05 апреля 2011

Это не просто и тоже не выглядит просто.В случае конфликта имен между двумя интерфейсами .NET должен спросить вас, какой интерфейс вы пытаетесь реализовать.Спросить вас об этом можно через ошибку неоднозначности.

Если бы у вас не было подобных ошибок, вы бы случайно реализовали интерфейсы.

0 голосов
/ 05 апреля 2011

Есть много ответов, и все они правы, поскольку явная реализация интерфейса - это решение вашей проблемы.

Я попытаюсь прояснить мотивы этого дизайна на несколько запутанном примере.:

Допустим, у меня есть интерфейс для людей, которые работают (с возможными реализациями, такими как LongDistanceRunner, Jogger, MarathonMan и т. Д.)

public interface IRunner 
{
   void Run();
}

и интерфейс для устройствкоторые можно включить и запустить (с возможными реализациями BathTub, Application, Dishwasher и т. д.)

public interface IRunnable
{
   void Run();
}

Теперь я хочу создать и интерфейс для IMusicallJogger (реализации, подобные JoggerWithIpod, BoomBoxJogger и т. Д.)

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.

Надеюсь, я помог уточнить концепцию.

0 голосов
/ 05 апреля 2011

Потому что то, что вы делаете, не правильно. A и B конфликтуют и имеют одно и то же имя для свойства ... вам нужно использовать Явную реализацию интерфейса.

Ссылка здесь .

...