Неоднозначность множественных обобщений - PullRequest
4 голосов
/ 27 декабря 2011

Коды ниже точно такие же, за исключением того, что один - C #, а другой - VB.Net. C # компилируется просто отлично, но VB.Net выдает предупреждение:

Интерфейс 'System.IObserver (Of Foo)' неоднозначен с другим реализован интерфейс «System.IObserver (Of Bar)» из-за «В» и Параметры «Out» в «Интерфейсе IObserver (Of In T)»

Почему VB.Net показывает предупреждение, а не C #? И самое главное, как я могу решить эту проблему?

Obs: я использую .Net Framework 4 с Visual Studio 2010 Ultimate.

Код VB.Net:

Module Module1

    Sub Main()

    End Sub

    Public Class Foo
    End Class
    Public Class Bar
    End Class
    Public Class Beholder
        Implements IObserver(Of Foo)
        Implements IObserver(Of Bar)

#Region "Impl"
        Public Sub OnCompleted() Implements System.IObserver(Of Bar).OnCompleted

        End Sub

        Public Sub OnError([error] As System.Exception) Implements System.IObserver(Of Bar).OnError

        End Sub

        Public Sub OnNext(value As Bar) Implements System.IObserver(Of Bar).OnNext

        End Sub

        Public Sub OnCompleted1() Implements System.IObserver(Of Foo).OnCompleted

        End Sub

        Public Sub OnError1([error] As System.Exception) Implements System.IObserver(Of Foo).OnError

        End Sub

        Public Sub OnNext1(value As Foo) Implements System.IObserver(Of Foo).OnNext

        End Sub
#End Region

    End Class

End Module

C # код:

 class Program {
        static void Main(string[] args) {
        }
    }

    public class Foo { }
    public class Bar { }
    public class Beholder : IObserver<Foo>, IObserver<Bar> {
        #region IObserver<Foo> Members

        public void OnCompleted() {
            throw new NotImplementedException();
        }

        public void OnError(Exception error) {
            throw new NotImplementedException();
        }

        public void OnNext(Foo value) {
            throw new NotImplementedException();
        }

        #endregion

        #region IObserver<Bar> Members


        public void OnNext(Bar value) {
            throw new NotImplementedException();
        }

        #endregion
    }

Ответы [ 2 ]

4 голосов
/ 27 декабря 2011

Подводя итог:

  • VB, похоже, выдает здесь ненужное предупреждение. Я сообщу об этом тестировщикам VB, когда они вернутся из рождественских каникул.
  • Это подозрительная практика программирования независимо от того, безопасна она или нет; Немного странно реализовывать две версии одного и того же интерфейса.
  • Если вместо этого вы выбрали ковариантный интерфейс, такой как IEnumerable<T>, то предупреждение будет оправдано. Если у вас есть объект, который представляет собой последовательность Черепах и последовательность Жирафов, то что произойдет, если вы неявно преобразуете его в последовательность животных? Вы получаете черепах или жирафов? Среда выполнения просто выбирает один, что не обязательно соответствует желаемому поведению.

Для некоторого интересного обсуждения последнего пункта см. Комментарии к моей статье 2007 года на эту тему:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/09/covariance-and-contravariance-in-c-part-ten-dealing-with-ambiguity.aspx

3 голосов
/ 27 декабря 2011

Это плохой дизайн для реализации обоих. Имейте два разных дочерних объекта, которые вы подписываете на двух наблюдателей. Я рекомендую иметь два дочерних объекта, каждый из которых реализует один из интерфейсов.

class Beholder
{
  public IObserver<Foo> FooObserver{get;private set;}
  public IObserver<Bar> BarObserver{get;private set;}
}

Когда противоречивость неоднозначна?

Тем не менее, я не вижу здесь непосредственной проблемы, поэтому предупреждение VB.net выглядит действительно странным для меня.

IObserver<in T> является противоположным вариантом. Таким образом, чтобы вызвать двусмысленность, вам нужно найти T, такие как IObserver<Foo> и IObserver<Bar> равны IObserver<T>.

Если оба Foo и Bar являются независимыми классами, таких T не существует, поскольку их нужно извлекать из обоих, чего не позволяет система типов .net.

Если бы любой из них был интерфейсом, возникла бы двусмысленность: просто создайте класс, производный от Foo и реализующий IBar.

Если бы один был получен от другого, это также было бы неоднозначно: если Foo получено из Bar, то IObserver<Bar> также IObserver<Foo>.

Когда со-дисперсия неоднозначна?

И, наконец, с ко-вариантными интерфейсами, такими как IEnumerable<T>, достаточно иметь общий базовый класс, в который оба преобразуются в ссылки. И Object выполняет это для любых двух классов (но не для типов значений).

Но IEnumerable<T> будет работать без ковариации, так как вам нужна последовательная реализация не универсального IEnumerable, а это невозможно для двух независимых классов.

...