【问题标题】:Multiple Generics ambiguity多泛型歧义
【发布时间】:2011-12-27 18:02:13
【问题描述】:

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

Interface 'System.IObserver(Of Foo)' 与另一个不明确 由于 'In' 和 'Interface 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会发出这个警告?或者为什么 C# 不给出这个警告?还是什么?
  • 两者都实现的设计很糟糕。有两个不同的子对象订阅两个观察者。
  • 嗯,VB.net 警告有点奇怪。接口是逆变的,Foo/Bar 是独立的类。我想不出任何TIObserver&lt;T&gt; 同时是IObserver&lt;Foo&gt;IObserver&lt;Bar&gt;
  • 埃里克,我已经编辑了这个问题。 CodeInChaos,起初我在考虑 Object 类型,但同样的问题发生在 IEnumerable 接口上,它是 Covariant...
  • @CodeInChaos 是对的; VB 警告似乎是一个错误。虽然这不一定是一个好的编程实践,但接口不可能统一。如果接口被标记为“out”而不是“in”,那么它们可以统一,从而产生实现定义的行为。

标签: c# .net vb.net generics generic-variance


【解决方案1】:

总结:

  • VB 似乎在这里不必要地发出警告。我会在 VB 测试人员从圣诞假期回来时向他们提及。
  • 无论是否安全,这都是一种可疑的编程做法;实现同一个接口的两个版本有点奇怪。
  • 如果您选择像IEnumerable&lt;T&gt; 这样的协变 接口,那么警告将是合理的。如果您的对象既是 Turtles 序列又是 Giraffes 序列,那么当您将其隐式转换为 Animal 序列时会发生什么?你有海龟还是长颈鹿?运行时只会选择一个,这不一定是您想要的行为。

有关最后一点的一些有趣讨论,请参阅我 2007 年关于该主题的文章中的 cmets:

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

【讨论】:

  • 那么,这应该被视为一个错误吗?我应该在 Connect 网站上报告吗?
  • @MarcelodeAguiar 尝试不会受到伤害。但是,如果它最终以“设计”的形式关闭,请不要感到惊讶。
  • @MarcelodeAguiar:我会说这是一个错误。如果需要,您可以在 Connect 上报告;我还将确保 VB 团队在下周每个人都回到办公室时知道这件事。这周有点像鬼城。我不希望很快得到修复;不完全正确的警告是低优先级。
  • 我看到了这个答案,因为它链接自我在 SO 上开始的新线程。 Lippert,你说得对,在这种情况下,逆变可能没有问题,因为FooBar,并且两者都不是从另一个派生的。但是,如果您检查我的问题(现在从这个线程链接)这两种类型是 interfaces,那么肯定某些类型可以继承这两个接口。问题就出现了。
【解决方案2】:

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

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

什么时候逆变不明确?

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

IObserver&lt;in T&gt; 是逆变的。因此,要引起歧义,您需要找到T,例如IObserver&lt;Foo&gt;IObserver&lt;Bar&gt; 都是IObserver&lt;T&gt;

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

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

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

什么时候协方差不明确?

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

但是IEnumerable&lt;T&gt; 在没有协方差的情况下会收支平衡,因为您需要非泛型IEnumerable 的一致实现,而这对于两个独立的类是不可能的。

【讨论】:

  • 如果AnimalCollection实现IEnumerable&lt;Animal&gt;,而Cat派生自Animal,那么CatCollection派生自AnimalCollection并实现IEnumerable&lt;Cat&gt;会有什么可疑之处,如果它自己的IEnumerable&lt;T&gt; 实现与继承的唯一区别在于后者将返回许多Animal 的实例,而这些实例恰好也是Cat, while the former would return references to those same instances, but with the compile-time type Cat` 的实例而不是Animal
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多