【问题标题】:Interface constraint on generic method arguments泛型方法参数的接口约束
【发布时间】:2012-04-14 13:00:03
【问题描述】:

为了正确理解 C#,我发现自己在问,在泛型方法参数上指定接口约束和简单地将接口指定为参数类型之间有什么实际区别?

public interface IFoo
{
    void Bar();
}

public static class Class1
{
    public static void Test1<T> (T arg1) where T : IFoo
    {
        arg1.Bar();
    }

    public static void Test2(IFoo arg1)
    {
        arg1.Bar();
    }
}

编辑

我知道我的示例非常狭窄,因为它只是一个示例。我对超出其范围的差异非常感兴趣。

【问题讨论】:

标签: c# generics


【解决方案1】:

在您的具体示例中没有区别。但采取以下方法:

public static class Class1
{
    public static T Test1<T>(T arg1) where T : IFoo
    {
        arg1.Bar();
        return arg1;
    }

    public static IFoo Test2(IFoo arg1)
    {
        arg1.Bar();
        return arg1;
    }
}

Test1 会返回 arg1 的具体类型,而Test2 只会返回接口。这通常用于流畅的界面。


扩展示例:

public interface IFoo
{
    void Bar();
}

public class Foo : IFoo
{
    // implementation of interface method
    public void Bar()
    {
    }

    // not contained in interface
    public void FooBar()
    {
    }
}


var foo = new Foo();
Class1.Test1(foo).FooBar(); // <- valid
Class1.Test2(foo).FooBar(); // <- invalid

【讨论】:

    【解决方案2】:

    您给出的示例没有任何区别。另一方面,使用通用版本使您能够在将来扩展约束列表 (where T : IFoo, IOther),而无需更改方法签名。

    【讨论】:

      【解决方案3】:

      我只想强调其他人给出的答案。

      Test(IFoo foo)Test&lt;T&gt;(T foo) where T : IFoo 之间存在差异。就像List&lt;object&gt;(或者,假设ArrayList,接收object)和List&lt;string&gt;之间存在巨大差异一样,这确实存在差异。

      Test (IFoo foo),为您提供多态性和类型继承的好处,就像List&lt;object&gt;。它允许您构建一个处理所有IFoo 类型的类。但有时我不只是想要多态性,我想要一个只能保存字符串的列表——List&lt;string&gt; 给了我这个,而不需要我在 ArrayList 上编写一个强类型包装器。

      您的代码也是如此。假设我有一个class Comparer&lt;T&gt; where T:IFoo。我希望能够使用此类将Foo1 对象相互比较,或将Foo2 相互比较,但我不想能够将Foo1Foo2 进行比较。强类型泛型方法将强制执行此操作,而多态方法则不会:

      public class Comparer
      {
          public bool Compare1<T>(T first, T second) where T : IFoo {...}
          public bool Compare2 (IFoo first, IFoo second) {...}
      }
      
      Foo1 first = new Foo1();
      Foo2 second = new Foo2();
      myComparer.Compare1(first, second); // won't compile!
      myComparer.Compare2(first, second); // Compiles and runs.
      

      【讨论】:

      • 这仍将编译:myComparer.Compare1&lt;IFoo&gt;(first, second);
      • 是的,但是您明确表示您将接受任何IFoo 对象,这很好。就像我可以打电话给myComparer.Compare1&lt;object&gt;("whatever", 5)一样。
      • 这不会编译,因为object 不是IFoo
      • 我说的是一般性:您可以指定更抽象的基类或接口,但这是一个显式操作,使Compare1 等同于Compare2
      【解决方案4】:

      这完全是类型转换的问题。如果您的方法必须返回 T,那么 Test1 将不需要强制转换,并且由于 Test2 仅返回一个接口,您将需要显式或隐式类型强制转换来获得最终类型。

      【讨论】:

        【解决方案5】:

        接口约束通常与例如IFoo, new()

        ...它允许您完全以T 操作对象,创建、初始化集合等 - 并按照建议返回 T。虽然只有接口,但您不知道它到底是哪个类 (T)。

        【讨论】:

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