4

下面的代码完全一样,只是一个是C#,另一个是VB.Net。C# 编译得很好,但 VB.Net 抛出警告:

由于 'Interface IObserver(Of In T)' 中的 'In' 和 'Out' 参数,接口 'System.IObserver(Of Foo)' 与另一个实现的接口 'System.IObserver(Of Bar)' 不明确

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

2 回答 2

4

加起来:

  • 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

于 2011-12-27T18:22:45.397 回答
3

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

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

什么时候逆变模棱两可?

我仍然没有看到直接的问题,所以 VB.net 警告对我来说确实很奇怪。

IObserver<in T>是逆变的。因此,要引起歧义,您需要找到T这样的 bothIObserver<Foo>IObserver<Bar>are IObserver<T>

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

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

如果一个派生自另一个,它也将是模棱两可的:如果Foo派生自Bar,那么IObserver<Bar>也是IObserver<Foo>

什么时候协方差不明确?

最后是协变接口,例如IEnumerable<T>拥有一个公共基类就足够了,两者都可以引用转换。并Object为任何两个类(但不是值类型)实现这一点。

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

于 2011-12-27T18:29:46.943 回答