第三章 C# 泛型
当两个模块功能相似,仅仅只是类型不同的时候,你会怎么办呢。请看以下代码:
public class IntClass { List<int> Intlist = new List<int>(); public void AddList(int i) { Intlist.Add(i); } } public void Main() { IntClass c1 = new IntClass(); c1.AddList(1); c1.AddList(1); }
但是当我们想c1.AddList("hello word");要插入一个字符串类型的时候,你会怎么做呢?
二逼程序员会这样做:
public class StringClass { List<string> Intlist = new List<string>(); public void AddList(string i) { Intlist.Add(i); } }
普通程序员会这样做:
public class ObjClass { List<object> Intlist = new List<object>(); public void AddList(object i) { Intlist.Add(i); } }
仔细想想,不断装箱拆箱是否效率低下呢,当我们要插入bool,char,datetime.....等等类型的时候,我们需要copy这么多类吗?这时我们就用到了泛型。
1.泛型的声明:
public class Class1<T> { private List<T> List = new List<T>(); public List<T> GetList() { return List; } public void AddList(T t) { List.Add(t); } }
2.泛型的默认值:
T t=defalut(T); 如果T是值类型,则t=0;如果T是引用类型,则t=null.
3.泛型约束:
接口约束:where T:Interface 必须显示的实现接口
基类约束:where T:BaseClass 参数必须为BaseClass或者派生类
类约束:where T:class T必须是引用类型
值类型约束: where T:struct T必须是值类型
构造函数约束: where T:new() 必须有默认的构造函数
泛型类型约束: Where T:T1
4静态成员:
不同类型的泛型可以认为是不同的类。 所有静态成员只能在同类中共享。
public class mm<T> { public static string Name{get;set;} }
mm<int> a,mm<int> b,mm<sting>c a,b可以共享Name,c不能
5.泛型类中的方法重载
方法的重载在.Net Framework中被大量应用,他要求重载具有不同的签名。在泛型类中,由于通用类型T在类编写时并不确定,所以在重载时有些注意事项,这些事项我们通过以下的例子说明:
public class Node<T, V>
{
public T add(T a, V b) //第一个add
{
return a;
}
public T add(V a, T b) //第二个add
{
return b;
}
public int add(int a, int b) //第三个add
{
return a + b;
}
}
上面的类很明显,如果T和V都传入int的话,三个add方法将具有同样的签名,但这个类仍然能通过编译,是否会引起调用混淆将在这个类实例化和调用add方法时判断。请看下面调用代码:
Node<int, int> node = new Node<int, int>();
object x = node.add(2, 11);
这个Node的实例化引起了三个add具有同样的签名,但却能调用成功,因为他优先匹配了第三个add。但如果删除了第三个add,上面的调用代码则无法编译通过,提示方法产生的混淆,因为运行时无法在第一个add和第二个add之间选择。
Node<string, int> node = new Node<string, int>();
object x = node.add(2, "11");
这两行调用代码可正确编译,因为传入的string和int,使三个add具有不同的签名,当然能找到唯一匹配的add方法。
由以上示例可知,C#的泛型是在实例的方法被调用时检查重载是否产生混淆,而不是在泛型类本身编译时检查。同时还得出一个重要原则:
当一般方法与泛型方法具有相同的签名时,会覆盖泛型方法。
泛型类的方法重写
方法重写(override)的主要问题是方法签名的识别规则,在这一点上他与方法重载一样,请参考泛型类的方法重载。
泛型的使用范围
本文主要是在类中讲述泛型,实际上,泛型还可以用在类方法、接口、结构(struct)、委托等上面使用,使用方法大致相同,就不再讲述。