一泛型优点
泛型类和泛型方法同时具备可重用性、类型安全和效率,这是非泛型类和非泛型方法无法具备的。以下依次对其进行详细说明。
1.类型安全
非泛型,如当ArrayList添加了一个字符类型时,存在一个字符向对象的隐式转换。同样,如果自列表检索一个字符类型对象时,存在一个自Object向字符类型的显示转换。
而检索类型对象时,是在运行时进行检查,因此,存在不安全性(如eg2非泛型,在运行时,将"e"转换为int时,会抛出类型转换的错误)。
相反,泛型如List<String> 允许程序在编译时而不是运行时对变量类型进行确定及检查(如eg1泛型).
2.效率
(1) 在JIT编译时而不是在执行过程中进行类型检查,性能会得到提高。
(2) 另外,对于值类型而言,使用非泛型需要对值类型进行装箱,拆箱,会浪费很多时间。
eg1泛型:
List<String> lis=new List<string>();
lis.Add("a");//不进行装箱。
lis.Add(System.DateTime);//在编译时便会报错,而不是在运行时才检查错误。
string a=lis[0];//不需要类型转换。
eg2非泛型:
public ArrayList AddToArrayList()
{
ArrayList arrlist =new ArrayList();
arrlist.Add(1);//装箱string-->object
arrlist.Add(2);
arrlist.Add("e");
return arrlist;
}
public void ReadFromArrayList(ArrayList arr)
{
int[] intvalue = new int[3];
for(int i=0; i<arr.Count; i++)
{
intvalue[i] = Convert.ToInt32(arr[i]);
}
}
3.可重用性
泛型能够实现对具有相同功能的代码重用。避免了重写代码带来的烦恼。
3.1需求描述
public classDog
{
public string Name { get; set; }
}
public classCat
{
public string Name {get; set; }
}
现在有一个需求,要求将Dog和Cat的名字打印出来,写两个方法,分别用于打印Dog和Cat的名字:
public staticvoid PrintDogName(Dog dog)
{
Console.WriteLine(dog.Name);
}
public staticvoid PrintCatName(Cat cat)
{
Console.WriteLine(cat.Name);
}
3.2合并方法
public staticvoid PrintName<T>(T t)
{
if (t is Dog)
{
Console.WriteLine((t as Dog).Name);
return;
}
if (t is Cat)
{
Console.WriteLine((t as Cat).Name);
return;
}
}
3.3最终优化
而(2)中方法是把方法合并成一个了,但总的代码量没有减少。只是把原来两个方法里的代码拿到一个方法里来,可以将Dog类和Cat类抽象出一个接口:
interface IName
{
string Name { get; set; }
}
public class Dog : IName
{
public string Name {get; set; }
}
public class Cat:IName
{
public string Name {get; set; }
}
//限制泛型:必须实现Iname接口。
public staticvoid PrintName<T>(T t)where T:IName
{
Console.WriteLine(t.Name);
}
二泛型应用
泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符可以具有一个或多个类型。
1泛型类
1.1简单泛型类定义
带有泛型参数列表。
public class Generic<T>
{
public T Field;
}
1.2 如何实例化泛型类
需指定实际类型来替换类型参数。这会建立一个新的泛型类,称为构造泛型类,选定的类型将替换所有出现的类型参数。
Generic<string> g = new Generic<string>();
g.Field = "A string";
2 泛型方法
2.1泛型方法定义
具有两个参数列表的方法:一个泛型类型参数列表和一个形参列表。类型参数可以作为返回类型或形参的类型出现。
2.2泛型方法解析
示例中泛型类型参数列表为<T>,形参列表为(T arg)。形参和返回类型都为泛型类型T。
T Generic<T>(T arg) { T temp = arg; ...}
2.3注意
只有当方法具有它自己的类型参数列表时,才能称其为泛型方法。在下面的代码中,只有方法 G是泛型方法。
class A
{
T G<T>(T arg) {...}
}
class Generic<T>
{
T M(T arg) {...}
}
3 泛型接口
3.1泛型接口定义
以常用泛型接口为例:System. IComparable<T>和 System. IEquatable< T> 。IComparable< T>接口定义 CompareTo 方法,该方法确定实现类型的实例的排序顺序。 IEquatable<T>接口定义Equals 方法,该方法确定实现类型的实例的相等性。示例中泛型类型参数列表为<T>。
public interfaceIEquatable<T>
{
bool Equals(T other);
}
3.2泛型接口使用
public classPerson : IEquatable<Person>
{
public Person(string lastName,string ssn)
{
this.SSN = ssn;
this.LastName = lastName;
}
public string SSN
{
get { return this.uniqueSsn; }
set
{
uniqueSsn = value;
}
}
public string LastName
{
get { return this.lName; }
set
{
lName = value;
}
}
public bool Equals(Person other) //继承接口的方法。
{
if (this.uniqueSsn == other.SSN)
return true;
else
return false;
}
}
public classTestIEquatable
{
public static void Main()
{
// Create a Person object for each job applicant.
Person applicant1 = new Person("Jone","1");
Person applicant2 = new Person("Jone","2");
applicants.Add(applicant1);
applicants.Add(applicant2);
// Create a Person object for the final candidate.
Person candidate = new Person("Jone","2");
Console.WriteLine("{0}({1}) already on file: {2}.",
applicant2.LastName,
applicant2.SSN,
applicant2.Equals(applicant1));
}
}
三泛型重点之泛型约束
1 泛型约束的概述
泛型约束的作用:对于所定义的泛型类,在客户端代码,可以对其实例化泛型类时所用的类型参数的类型种类施加限制。若在客户端代码中,尝试使用某个约束所不允许的类型来实例化类,则会产生编译错误。
约束的类型有以下六种:
约束 | 说明 |
T:结构 | 类型参数必须是值类型。可以指定除Nullable 以外的任何值类型。 |
T:类 | 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 |
T:new() | 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 |
T:U | 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。 |
2.泛型约束的演练
2.1代码模板:
class MyList<T> where T:new ()
{
List<T> list = new List<T>();
public T this[int i]
{
get { return list[i]; }
set { this.list[i] = value; }
}
}
class Person
{
public string Name { get; set; }
}
2.2指定泛型参数为值类型
(1)根据2.1代码模板,指定泛型类型为值类型。
class MyList<T> where T:struct
{
}
(2) 在客户端实例化泛型类型:
MyList<Person> list = new MyList<Person>();
出现编译错误,提示:
类型“GencConstraint.Person”必须是不可为 null 的值类型才能用作泛型类型或方法“GencConstraint.MyList<T>”中的参数“T”。
(3) 在客户端使用如下方式的实例化,将一切正常:
MyList<int> list = new MyList<int>();
2.3指定泛型参数为引用类型
(1)根据2.1代码模板,指定泛型类型为引用类型。
class MyList<T> where T:class
{
}
(2)在客户端使用如下方式的实例化,将一切正常:
MyList<Person> list = new MyList<Person>();
(3)在客户端使用如下方式的实例化,出现编译错误:
MyList<int> list = new MyList<int>();
2.4指定泛型参数为无参的公共的构造函数
(1)根据2.1代码模板,指定泛型类型如下:
class MyList<T> where T:new()
{
}
(2)为Person类指定私有构造函数:
class Person
{
public string Name { get; set; }
private Person()
{
//do nothing
}
}
出现编译错误,提示:
“GencConstraint.Person”必须是具有公共的无参数构造函数的非抽象类型,才能用作泛型类型或方法“GencConstraint.MyList<T>”中的参数“T”。
2.5指定泛型参数必须派生于指定基类
(1)新增基类:
abstract class SeniorAnimal//高级动物
{
public abstract void Speak();//会说话
}
(2)根据2.1代码模板,指定泛型参数必须派生于基类SeniorAnimal:
class MyList<T> where T: SeniorAnimal
{
}
(3) 在客户端实例化泛型类型:
MyList<Person> list = new MyList<Person>();
出现编译错误,提示:
不能将类型“GencConstraint.Person”用作泛型类型或方法“GencConstraint.MyList<T>”中的类型参数“T”。没有从“'GencConstraint.Person”到“GencConstraint.SeniorAnimal”的隐式引用转换。
(4)修改代码,使Person类继承自SeniorAnimal类:
class Person : SeniorAnimal
{
public string Name { get; set; }
public override void Speak()
{
Console.WriteLine("我会说英语!");
}
}
则编译成功。
2.6指定泛型参数必须实现指定接口。
示例同2.4,不再讲述。
2.7指定泛型参数必须派生于泛型类型U(裸类型约束)
格式如下:
class MyList<U> where U : SeniorAnimal
{
List<U> list = new List<U>();
public void ShowInfo<T>() where T : U
{
}
}