4

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

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

Why does VB.Net show the warning and not C#? And most important, how can I resolve this problem?

Obs: I'm 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
    }
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
Marcelo de Aguiar
  • 1,432
  • 12
  • 31
  • 1
    "Why?" Your question is ambiguous. Are you asking Why does VB give that warning? Or why does C# not give that warning? Or what? – Eric Lippert Dec 27 '11 at 18:10
  • 1
    It's bad design to implement both. Have two different child objects which you subscribe to the two observers. – CodesInChaos Dec 27 '11 at 18:10
  • 1
    hmm the VB.net warning is a bit strange. The interfaces are contravariant and `Foo`/`Bar` are independent classes. I can't think of any `T` for which `IObserver` is `IObserver` and `IObserver` at the same time. – CodesInChaos Dec 27 '11 at 18:15
  • Eric, I have edited the question. CodeInChaos, At first I was thinking of Object type, but the same problem happens with IEnumerable interface, wich is Covariant... – Marcelo de Aguiar Dec 27 '11 at 18:18
  • @CodeInChaos is right; the VB warning appears to be an error. Though this is a not necessarily a good programming practice, there's no possibility that the interfaces will unify. If the interfaces were marked as "out" instead of "in" then they could unify and thereby produce implementation-defined behaviour. – Eric Lippert Dec 27 '11 at 18:18
  • @MarcelodeAguiar On `IEnumerable` this warning would make sense, since `IEnumerable` and `IEnumerable` are both `IEnumerable`, exactly because `IEnumerable` is covariant. And it would also make sense with the contravariant `IObserver` if either of them were an interface, or if one were the base class of the other. – CodesInChaos Dec 27 '11 at 18:23
  • The two codes doesn't even match, in vb you have different Sub names for the implementation of FOO and BAR. – BigL Dec 27 '11 at 18:32
  • @BigL I don't think that's responsible for this problem. Looks just like language specific issues with interface implementation. – CodesInChaos Dec 27 '11 at 18:35

2 Answers2

4

Summing up:

  • VB appears to be giving the warning unnecessarily here. I'll mention it to the VB testers when they're back from Christmas vacation.
  • This is a suspicious programming practice regardless of whether it is safe or not; it's a bit strange to implement two versions of the same interface.
  • If instead you chose a covariant interface like IEnumerable<T> then the warning would be justified. If you have an object that is both a sequence of Turtles and a sequence of Giraffes, then what happens when you implicitly convert it to sequence of Animal? Do you get Turtles or Giraffes? The runtime just picks one, which is not necessarily the behaviour you want.

For some interesting discussion of the last point see the comments to my 2007 article on the subject:

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

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • So, should this be treated as a bug? Should I report it at Connect site? – Marcelo de Aguiar Dec 28 '11 at 01:57
  • @MarcelodeAguiar Trying can't hurt. But don't be surprised if it ends up closed as "by design". – CodesInChaos Dec 28 '11 at 10:48
  • @MarcelodeAguiar: I'd say its a bug. You can report it at Connect if you want; I will also make sure that the VB team knows about it when everyone is back in the office next week. It's a bit of a ghost town this week. I would not expect a fix any time soon; not-quite-correct warnings are a low priority. – Eric Lippert Dec 28 '11 at 15:05
  • I see this answer as it's linked from a new thread I started here on SO. Lippert, you're right there could be no problem with contravariance in this case because `Foo` and `Bar` are **classes**, and neither derives from the other. But if you check my question (now linked from this thread) where the two types are **interfaces**, then surely some type could inherit both interfaces. And the problem arises. – Jeppe Stig Nielsen Nov 27 '12 at 19:04
3

It's bad design to implement both. Have two different child objects which you subscribe to the two observers. I recommend having two child objects, with each implementing one of the interfaces.

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

When is contra-variance ambiguous?

Still I don't see an immediate problem here, so the VB.net warning looks indeed strange to me.

IObserver<in T> is contra-variant. So to cause an ambiguity you'd need to find a T such both IObserver<Foo> and IObserver<Bar> are IObserver<T>.

If both Foo and Bar are independent classes, no such T exists, since it's need to derive from both of them, which the .net type system doesn't allow.

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

If one derived from the other, it'd be ambiguous too: if Foo derived from Bar, then IObserver<Bar> is also IObserver<Foo>.

When is co-variance ambiguous?

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

But IEnumerable<T> would break even without covariance, since you need a consistent implementation of the non generic IEnumerable, and that's not possible for two independent classes.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • If `AnimalCollection` implements `IEnumerable`, and `Cat` derives from `Animal`, would there be anything dubious about `CatCollection`, deriving from `AnimalCollection` and implementing `IEnumerable`, if the only difference between its own `IEnumerable` implementation and the inherited one was that the latter would return a number of instances of `Animal` that all happened to also be instances of `Cat, while the former would return references to those same instances, but with the compile-time type `Cat` rather than `Animal`? – supercat Dec 10 '12 at 22:04