1 概述
1.1 引入泛型方法
在某些情况下,一个类型中可能只有少数方法成员用到了类型参数,这时就未必需要将整个类型都定义成为泛型。例如在下面的代码中,泛型类GC<T>定义了一个静态方法Contain,用于判断一个元素是否存在于一个数组之中:
public class GC<T> { //静态字段 static readonly double PI=3.14; //方法 public static bool Contain(T[] ts,T tp) { foreach(T t1 in ts) { if(t1.Equals(tp)) return true; } return false; } }
在每次调用该方法时,需要指定该泛型类的一个封闭的构造类型:
short[] sArray = new short[] {1,3,15,255}; bool b1 = GC<short>.Contain(sArray,short.MaxValue);//false int [] iArray = new int[]{1,3,15,255,65535}; bool b2=GC<int>.Contain(iArray,ushort.MaxValue);//true
泛型类GC<T>中定义了一个静态字段PI。由于静态成员属于泛型类的构造类型所有,所以对于每一个构造类型,都为该字段分配了存储空间。
而下面的程序将类型参数从类的定义中转移到方法的定义中,这就是泛型方法:
class GenericsMethodSample { static void Main() { short[] sArray = new short[] { 1, 3, 15, 255 }; Console.WriteLine(C.Contain<short>(sArray, short.MaxValue));//false int[] iArray = new int[] { 1, 3, 15, 255, 65535 }; Console.WriteLine(C.Contain<int>(iArray, ushort.MaxValue));//true } } /// <summary> /// 泛型方法类 /// </summary> public class C { //静态字段 static readonly double PI = 3.14; //泛型方法 public static bool Contain<T>(T[] ts, T tp) { foreach (T t1 in ts) { if (t1.Equals(tp)) return true; } return false; } }
上面程序中,两次方法的调用都是通过同一个类进行的,静态字段PI在内存中只会占用一个存储。
1.2 泛型方法的定义
定义泛型方法也是在方法名之后将类型参数包含在一对分隔符<>中。如果有多个类型参数,则相互间用“,”号分割。之后,所定义的类型参数既可以作为方法的参数类型和返回类型,也可以用来在方法的执行代码中定义局部变量。除此之外,泛型方法的定义规则与普通方法的定义规则相同。
如果在同一个类型中定义了多个泛型方法,它们的类型参数是互不相关的。
如果泛型方法属于一个泛型类型,而二者又定义了相同的类型参数,那么类型参数的使用也服从“屏蔽规则”,即泛型方法中出现的类型参数属于该方法的定义,而在类型的其它成员中出现的类型参数属于类的定义。例如:
public class GArith<T> { private T[] m_list; public static void Swap<T>(ref T tp1,ref T tp2) { ... } }
这时为泛型类GArith<T>创建任何一个构造类型的实例,其私有有字段m_list类型都被替换为相应的封闭类型,但不会影响到泛型方法Swap<T>的参数类型。同样,指定给方法的封闭类型也和类的构造类型无关。例如:
int x=2; int y=5; GArith<string>.Swap<int>(ref x,ref y);//correct Garith<int>.Swap<string>(ref x,ref y);//error:传递类型与实际类型不同
所以,为了提高程序的可读性,应尽量避免为泛型类型及其泛型的成员方法定义同名的类型参数。
在泛型方法中同样可以对类型参数进行限制,限制方式和泛型类的相同
public static T Max<T>(T tp1,T tp2) where T : IComparable<T> { if(tp1.CompareTo(tp2) > 0) return tp1; else return tp2; }
泛型方法既可以属于普通类型,也可以属于泛型类型(泛型类、泛型结构、泛型接口)。
C#中不允许定义泛型的属性、事件、索引函数和操作符。