1、泛型概述
使用泛型可以创造出更灵活的类,定义这种参数化类,可以使内部算法相同,但数据类型会跟随类型参数而变。
优点:
- 类型安全:指定类型参数后,编译时会进行类型检查。
- 性能优化:减少性能消耗,避免装箱和拆箱操作。
- 代码重用:避免编写大量重复的代码。
2、泛型类的使用
2.1 定义泛型类
public class Stack<T>
{
private T[] _Items;
public void Push(T data)
{
... ...
}
}
2.2 使用泛型类
class Program
{
public void main()
{
Stack<int> path = new Stack<int>();
path.Push(10);
}
}
3、泛型的其他使用方式
3.1 默认值
在使用泛型定义类型时,不能用null对类型进行初始化,因为泛型也可能被实例化为值类型。可以使用default解决这个问题,default自动为引用类型初始化null,为值类型初始化0。
public void Push(T data)
{
T test = default(T);
... ...
}
3.2 接口与结构
C#支持在语言中全面使用泛型,其中包括接口和结构,使用语法与类的语法完全相同
interface IPair<T>
{
T First { get; set; }
T Second { get; set; }
}
3.3 多个类型参数
interface IPair<TFirst, TSecond>
{
... ...
}
4、约束
在定义泛型时,可以对实例化类时提供的类型参数施加限制。如果创建实例时尝试使用约束不允许的类型,则会发生编译错误。
创建约束的语法:class 类名<类型参数> where 类型参数:约束
4.1 引用类型约束
class Sample<T> where T : class
表示T必须是引用类型,包括interface、数组等。
4.2 值类型约束
class Sample<T> where T : struct
表示T必须是值类型,包括int、char、struct等。
4.3 类类型约束
class Sample<T> where T : Stream
表示T必须是Stream类或者Stream的子类
Sample<Stream> 有效,符合约束
Sample<String>无效,String无法通过引用或装箱拆箱强制转换为Stream
4.4 接口约束
class Sample<T> where T : IDisposable
表示T必须实现了IDisposable接口
Sample<SqlConnection> 有效,SqlConnection实现了IDisposable接口
Sample<StringBuilder> 无效,StringBuilder没有实现IDisposable接口
4.5 构造函数约束
class Sample<T> where T : Stream , new()
表示T必须是Stream或Stream的子类,并且必须提供无参的构造函数
4.6 组合约束
对类型参数的约束可以是多个,但要注意以下几点:
- 约束只能是一种类型,所以,struct和class不能同时存在。
- 每个值类型都提供默认构造函数,所以,struct和new()不能同时存在。
- class必须是第一个约束。
- new()必须是最后一个约束。
- 类必须在接口的前面。
4.7 约束的限制
- 不支持用约束限制类必须实现特定的方法或操作符,所以泛型类型不能使用操作符。
- 假如类型参数提供多个约束,编译器认为不同的约束之间是AND关系,不能在约束之间指定OR关系
- 委托类型、数组类型和枚举类型不能在约束中使用,因为他们都是“密封”类型。
- 构造函数约束只针对默认构造函数,不支持有参数构造函数。