The ambiguity of multiple generics

The codes below are exactly the same, except that one is C # and the other is VB.Net. C # compiles just fine, but VB.Net gives a warning:

The interface 'System.IObserver (Of Foo)' is ambiguous with another implemented interface 'System.IObserver (Of Bar)' due to the 'In' and the 'Out' parameters in the 'IObserver (Of In T) Interface'

Why is VB.Net showing a warning and not C #? And most importantly, how can I solve this problem?

Discussion: I am using .Net Framework 4 with Visual Studio 2010 Ultimate.

VB.Net Code:

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 # code:

  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 } 
+4
generics c # generic-variance
Dec 27 '11 at 18:02
source share
2 answers

Summarizing:

  • VB seems to give a warning unnecessarily. I will tell VB testers about this when they return from the Christmas holidays.
  • This is a suspicious programming practice, whether it is safe or not; it’s a little strange to implement two versions of the same interface.
  • If instead you chose a covariant interface, for example IEnumerable<T> , then the warning will be justified. If you have an object that is both a series of turtles and a sequence of Giraffes, then what happens when you implicitly convert it to an Animal sequence? Do you get Turtles or Giraffes? Runtime simply selects one, which is optional at will.

For some interesting discussion of the last paragraph, see the comments on my 2007 related article:

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

+4
Dec 27 '11 at 18:22
source share

Bad design to implement both. You have two different child objects that you subscribe to two observers. I recommend having two children, each of which implements one of the interfaces.

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

When is the controversy ambiguous?

However, I do not see an immediate problem here, so the VB.net warning looks really strange to me.

IObserver<in T> is contravariant. Therefore, to cause ambiguity, you need to find T , such as IObserver<Foo> and IObserver<Bar> are IObserver<T> .

If both Foo and Bar are independent classes, such a T does not exist, since it must be deduced from both of them, which does not allow a system of type .net.

If each of them were an interface, there would be ambiguity: just create a class that derives from Foo and implements IBar .

If it was obtained from another, it would also be ambiguous: if Foo obtained from Bar , then IObserver<Bar> also IObserver<Foo> .

When is the combination ambiguous?

And finally, with co-variant interfaces such as IEnumerable<T> , it’s enough to have a common base class, for which both are reference quotes. And Object does this for any two classes (but not for value types).

But IEnumerable<T> will break even without covariance, because you need a consistent implementation of non-generic IEnumerable , and this is not possible for two independent classes.

+3
Dec 27 '11 at 18:29
source share



All Articles