【问题标题】:C# - generic methods vs. non-generic methodsC# - 泛型方法与非泛型方法
【发布时间】:2012-12-19 19:08:51
【问题描述】:

我对为什么/何时想要使用泛型方法感到有些困惑,因为非泛型方法可以访问其包含类的泛型成员并传递泛型参数。

所以,使用一个可能没有抓住重点的罐头示例(但强调了我问这个问题的原因),我为什么要这样做:

public class SomeGeneric<T>
{
    public T Swap<T>(ref T a, ref T b)
    {
        T tmp = a;
        a = b;
        b = tmp;
    }
}

结束

public class SomeGeneric<T>
{
    public T Swap(ref T a, ref T b)
    {
        T tmp = a;
        a = b;
        b = tmp;
    }
}

这个?

或者,真的,我为什么要使用通用方法

【问题讨论】:

  • 在大多数情况下,要么方法类是通用的。很少两者兼而有之。在您的第一个示例中,这实际上是一个问题,因为 T 在类和方法定义之间不明确;它们应该有不同的名称,但从语义上讲,您不希望使用不同的类型。可能需要两个通用参数,一个在类,一个在方法级别,这只是......很少见。

标签: c# generics


【解决方案1】:

方法级别类型参数的常见场景是扩展方法,因为它们必须在非泛型静态类中声明。但它们对于非泛型类型中的每个泛型成员都是必需的。

public static class Extensions
{
    public static void Foo<A, B>(this A a, B b) { [...] }

    public static T Bar<T>(this String input) { [...] }

    public static U FooBar<V, W>(this V v, W w) { [...] }
}

【讨论】:

    【解决方案2】:

    您通常会在非泛型类型中使用泛型方法。

    例如,查看Enumerable class。它为大多数 LINQ 功能定义了通用扩展方法,但它本身不是通用的。

    您可能还需要泛型类型中的泛型方法,但前提是该泛型方法使用了不同 泛型类型说明符。

    这使您可以编写如下内容:

     class Foo<T> where T : IConvertible, IComparable<T>
     {
          int CompareTo<U>(U other) where U : IConvertible
          {
               // Convert to this
               T otherConverted = Convert.ChangeType(other, typeof(T));
               return this.CompareTo(otherConverted);
          }
     }
    

    (当然,这有点做作,但与 double 相比,Foo&lt;int&gt; 可以正确编译和工作,等等)

    【讨论】:

      【解决方案3】:

      如果包含的类不是泛型的怎么办?如果它有不同的泛型类型参数怎么办?

      【讨论】:

        【解决方案4】:

        第一个例子没有多大意义,因为没有使用类参数。不过考虑另一个例子:

        public class SomeGeneric<T>
        {
            public K ConvertTo<T>(T a)
            {
                 return CodeThatConvertsTtoK(a);
            }
        }
        

        及其用法: new SomeGeneric&lt;int&gt;().ConvertToInt("ten");

        【讨论】:

        • 应该是public K ConvertTo&lt;K&gt;(T a)(编译器会按原样抱怨)
        【解决方案5】:

        如果类和方法都是泛型的,那么类型参数(“泛型参数”)当然必须有不同的名称。在您的第一个示例中,不能有两个名为 T 的不同事物。

        如果您的方法是非静态的(看起来),如果您选择将包含的类设为泛型,则必须在实例化您的类时已经指定类型。喜欢var obj = new SomeGeneric&lt;DateTime&gt;();。所以它应该是逻辑上“属于”类建模的对象的东西。

        如果您的方法是静态的,并且您选择使类成为泛型,您仍然必须以某种方式与类一起指定类型参数。如果从类外部调用该方法,它会像SomeGeneric&lt;DateTime&gt;.Swap(ref a, ref b);

        使 方法 通用的优点是,在许多情况下,您可以使用 类型推断,它允许您省略尖括号类型参数。您只能使用通用方法来做到这一点。示例:nonGeneric.Swap(ref a, ref b); 其中Swap&lt;T&gt; 方法是通用的。编译器将查看 ab 的编译时类型,并找出适合 T 的类型,而无需您指定。

        结论:如果T在逻辑上不属于类(如List&lt;T&gt;),则放在方法中。

        【讨论】:

          【解决方案6】:

          这是一个泛型方法真正大放异彩的例子。考虑一个表达式,如1+2,表示为二叉树。您想在整个树上实现访问者模式,目标是某种 map/reduce 操作。一些例子是:

          • 将表达式简化为 string 以打印它
          • 将表达式简化为 double 以计算其值
          • 将一个表达式映射到另一个表达式,其中一些成员已更改/添加/删除

          所有这些操作都可以放在访问者模式方法后面:

          public abstract class Expression
          {
            public abstract T Reduce<T>(ITransformer<T> transformer);
          }
          

          这与经典的访问者实现类似,但术语发生了变化:我们用Reduce() 代替了Accept(),用ITransformer&lt;T&gt; 代替了IVisitor。请注意,该方法是通用的。

          这种方法允许我们创建任意数量的ITransformer&lt;T&gt; 类,将层次结构转换为任何类型 T,支持 map-reduce 操作。

          【讨论】:

            猜你喜欢
            • 2011-04-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-16
            • 2015-07-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多