多泛型歧义

本文关键字:歧义 泛型 | 更新日期: 2023-09-27 18:18:49

下面的代码是完全相同的,除了一个是c#,另一个是VB.Net。c#编译得很好,但是VB。Net抛出警告:

接口的系统。observer (Of Foo)'与另一个有歧义实现接口系统。IObserver(Of Bar)’,因为有"In"和'接口IObserver(Of in T)'中的'Out'参数

为什么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
    }

多泛型歧义

总结:

  • 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

两者同时实现是糟糕的设计。有两个不同的子对象,分别订阅两个观察者。我建议使用两个子对象,每个子对象实现一个接口。

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>

如果FooBar都是独立的类,则不存在这样的T,因为它需要从它们两个派生,这是。net类型系统不允许的。

如果它们中的任何一个是接口,就会产生歧义:只创建一个从Foo派生并实现IBar的类。

如果其中一个衍生自另一个,那么它也是不明确的:如果Foo衍生自Bar,那么IObserver<Bar>也是IObserver<Foo>

协方差何时有歧义?

最后,对于协变接口,如IEnumerable<T>,有一个公共基类就足够了,这两个基类都是可引用转换的。Object为任意两个类(但不包括值类型)实现了这一点。

但是如果没有协方差,IEnumerable<T>就会收支平衡,因为您需要非泛型IEnumerable的一致实现,而这对于两个独立的类是不可能的。