9

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

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

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;
    }
}

这个?

或者,真的,我为什么要使用泛型方法?

4

6 回答 6

9

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

例如,查看Enumerable。它为大多数 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);
      }
 }

(当然,这有点做作,但确实可以编译并正常工作以Foo<int>与 adouble等进行比较)

于 2012-12-19T19:10:03.160 回答
4

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

于 2012-12-19T19:09:53.930 回答
2

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

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

及其用法: new SomeGeneric<int>().ConvertToInt("ten");

于 2012-12-19T19:14:08.873 回答
1

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

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

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

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

结论:如果T逻辑上不属于类(如 中List<T>),则将其与方法放在一起。

于 2012-12-19T20:23:02.340 回答
0

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

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) { [...] }
}
于 2012-12-19T19:14:26.513 回答
0

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

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

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

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

这与经典的 Visitor 实现类似,但术语发生了变化:我们有Reduce()代替Accept()和一个ITransformer<T>代替一个IVisitor。请注意,该方法是通用的。

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

于 2021-09-07T21:55:48.460 回答