【问题标题】:Difference between generic argument constrained to an interface and just using the interface约束到接口的泛型参数与仅使用接口之间的区别
【发布时间】:2010-10-10 08:43:54
【问题描述】:

这有什么区别:

void MyMethod(IMyInterface value)
{
    //...
}

还有这个:

void MyMethod<T>(T value) where T : IMyInterface
{
    //...
}

【问题讨论】:

    标签: .net generics interface


    【解决方案1】:

    主要的功能区别是您可以知道泛型方法内部对象的实际类型。 T 参数将包含在某些情况下可能有利的实际类型。

    在非泛型情况下,您不能保证访问对象的底层类型。您可以获取大多数类型 value.GetType() 但用户可以传递 Null 并阻止您。

    【讨论】:

    • 这是不正确的,他将 T 限制为 IMyInterface。
    • @joshperry,是的,但 T 仍将指向传入值的实际类型。更准确地说,它将指向方法调用处的引用类型。我 100% 确信我在这一点上是正确的。
    • 好吧,98 % ;-p 如果调用者使用了 implicit 泛型类型推断,那么 T 将是 variable 的类型(不是一定是引用,因为引用可能是子类)。如果调用者使用了 explicit 表示法,那么它们可能有不同的变量、T 和实例。
    • @joshperry,不需要道歉。昨晚,我花了整整 10 分钟的时间,用几个例子输入了一个问题的整页答案。然后乔尔指出我误读了这个问题,并为另一个问题提供了答案。我最终将答案保留为 WIKI
    • @Marc,doh,忘了那个。
    【解决方案2】:

    Jared 提到了一些要点;另一个有趣的:使用泛型,你可以避免值类型的装箱只要你基本上不碰它......所以我可以有一个struct Foo : IMyInterface并将它传递进去,然后它不会被装箱。

    集合之类的差异变得更加明显:

    static void Foo(IEnumerable<IMyInterface> data) {}
    

    static void Foo<T>(IEnumerable<T> data) 
        where T : IMyInterface {}
    

    现在,由于 C# 3.0 没有协方差(数组除外),我无法将 List&lt;Bar&gt; 传递到顶部,即使 Bar : IMyInterface - 但我可以使用第二个(隐式 T = Bar )。

    【讨论】:

    • IEnumerator&lt;T&gt; 更有趣,因为一些更常见的枚举器实际上是结构体。
    • @supercat 是的,但是如果您使用IEnumerable&lt;T&gt;.GetEnumerator(),则不能使用它;它只会将该版本用于 foreach 而不是事物本身
    • 如果您使用IEnumerator&lt;T&gt;.GetEnumerator(),那是正确的。但是,如果您说using(var myEn = myList.GetEnumerator(); doSomething(ref myEn);doSomething 采用IEnumerator&lt;T&gt;-约束泛型,它可以将事物作为结构接收(请注意,如果不小心总是通过ref 传递它,语义将与裸接口类型不同;此外,泛型类型的分配可能会获取枚举状态的快照(适用于某些结构类型的枚举器,但不能适用于任何类类型的枚举器)。
    【解决方案3】:

    通用版本需要 .NET 2.0。

    但是说真的,虽然它们看起来很相似,但它们之间存在根本差异。一个区别是,在运行时,JIT 编译器将为将用于通用版本的每种值类型生成代码。非泛型版本需要将值类型装箱才能传递给函数。

    在处理代表时,差异也很重要。 MyMethod&lt;int&gt; 的签名匹配 void MyDelegate(int x) 而非泛型版本不匹配。

    【讨论】:

      【解决方案4】:

      另一个区别是,使用泛型方法允许您指定对象必须实现的多个接口:

      class Dictionary<TKey,TVal>
          where TKey: IComparable, IEnumerable
          where TVal: IValue
      { ... }
      

      【讨论】:

        【解决方案5】:

        另一个细微的区别是您不能仅在约束上重载方法(约束不是方法签名的一部分):

        这是非法的:

        void MyMethod<T>(T value) where T : IMyInterface
        {
            //...
        }
        
        void MyMethod<T>(T value) where T : IMyInterface2
        {
            //...
        }
        

        虽然这是合法的:

        void MyMethod(IMyInterface value)
        {
            //...
        }
        
        void MyMethod(IMyInterface2 value)
        {
            //...
        }
        

        【讨论】:

          【解决方案6】:

          在这种情况下要考虑的另一个警告是,在泛型中使用“where T : ”可能会过度使用,从而使您的泛型类型本质上是非泛型的。

          IE:请记住,通过将泛型方法隔离到 IMyInterface,您将该方法隔离到仅实现 IMyInterface 的那些类型。因此,如果您只是根据良好的 OOP 原则选择使用 IMyInterface,但在任何地方都只有一个(或在某些情况下是极少数)潜在类型来实现该接口,那么您就违背了目的使用泛型。在这种情况下,第一种选择会更好。

          仅当您将拥有更多实际实现 IMyInterface 的类型时,才在泛型类型上使用“where”。

          【讨论】:

            【解决方案7】:

            一般来说,泛型方法的另一个区别(尽管不是你的例子)是,如果一个方法像T MungeThing&lt;T&gt;(T it) where T:IMungeable&lt;T&gt; 和类Fnord 实现IMungeable&lt;Fnord&gt;,那么代码将能够说:Fnord thing1, thing2; ... thing1 = MungeThing(thing2);并且编译器会知道MungeThing 将返回Fnord 而不是IMungable 的任意实现。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-06-11
              • 1970-01-01
              • 2020-09-16
              • 2018-04-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多