定义泛型方法

  上个示例中使用了方法GetCows()。在讨论这个示例时也提到,可以使用泛型方法得到这个方法的更一般形式。本节将说明如何达到这一目标。在泛型方法中,返回类型和/或参数类型由泛型类型参数来确定。例如:

  1. public T GetDefault<T>()
  2. {
  3. return default(T);
  4. }

  这个小示例使用本章前面介绍的default关键字,为类型T返回默认值。这个方法的调用如下所示:

  1. int myDefault = GetDefault<int>();

  在调用该方法时提供了类型参数T。

  这个T与用于给类提供泛型类型参数的类型差异极大。实际上,可以通过非泛型类来实现泛型方法:

  1. public class Defaulter
  2. {
  3. public T GetDefault<T>()
  4. {
  5. return default(T);
  6. }
  7. }

  但如果类是泛型的,就必须为泛型方法类型使用不同的标识符。下面的代码不会编译:

  1. public class Defaulter<T>
  2. {
  3. public T GetDefault<T>() // ❌
  4. {
  5. return default(T);
  6. }
  7. }

  必须重命名方法或类使用的类型T。

  泛型方法参数可以采用与类相同的方式使用约束,在此可以使用任意的类类型参数,例如:

  1. public class Defaulter<T1>
  2. {
  3. public T2 GetDefault<T2>()
  4. where T2 : T1
  5. {
  6. return default(T2);
  7. }
  8. }

  其中,为方法提供的类型T2必须与给类提供的T1相同,或者继承自T1。这是约束泛型方法的常用方式。

  在前面的Farm<T>类中,可以包含下面的方法:

  1. public Farm<U> GetSpecies<U>() where U : T
  2. {
  3. Farm<U> speciesFarm = new Farm<U>();
  4. foreach(T animal in animals)
  5. {
  6. if(animal is U)
  7. {
  8. speciesFarm.Animals.Add(animal as U);
  9. }
  10. }
  11. return speciesFarm;
  12. }

  这可以替代GetCows()和相同类型的其他方法。这里使用的泛型类型参数U由T约束,T又由Farm<T>类约束为Animal。因此,如果愿意,可以把T的实例视为Animal的实例。

  在Program.cs中,使用这个新方法需要进行一处修改:

  1. Farm<Cow> dairyFarm = farm.GetSpecies<Cow>();

  也可以编写如下代码:

  1. Farm<Chicken> poultryFarm = farm.GetSpecies<Chicken>();

  对于继承了Animal的其他类,都可以使用这种方法。

  这里要注意,如果某个方法有泛型类型参数,会改变该方法的签名。也就是说,该方法有几个重载版本,它们仅在泛型类型参数上有区别。例如:

  1. public void ProcessT<T>(T op1)
  2. {
  3. ...
  4. }
  5. public vid ProcessT<T, U>(T op1)
  6. {
  7. ...
  8. }

  使用哪个方法取决于调用方法时指定的泛型类型参数的个数。