Framework类库中的泛型
泛型可以使代码重用,提高开发效率
CLR允许在引用类型、值类型或接口中定义泛型方法;
CLR允许创建泛型引用类型、泛型值类型(枚举不允许创建)、泛型委托类型和泛型接口类型;
System.Collections.Generics中定义了List<T>,<T>表示它操作的是一个未指定数据类型;定义泛型类型或方法时,T是一个变量名,在源代码中能够使用一个数据类型的任何位置都能用T,例如方法参数、返回值等等。
泛型参数变量要么称为T,要么以T开头(如Tkey,TEvent...)
定义好泛型类型后,使用泛型类型或方法时,制定具体的类型实参
var validationResult = new List<ValidationResult>();
泛型的优势
- 源代码保护
使用泛型算法时候,不需要了解算法内部的具体实现 - 类型安全
将泛型算法运用于一个具体的类型,只有与数据类型兼容算法的对象才能使用算法,若不兼容,便会编译错误或运行异常 - 更加清晰的代码
由于编译器强制类型安全,减少了代码中的转型次数 - 更佳的性能
由于装箱会造成性能的浪费,通过泛型避免了装箱下面是一段泛型与非泛型的算法性能测试对比
class Program { static void Main(string[] args) { ValueTypePreTest(); ReferenceTypePreTest(); Console.ReadKey(); } private static void ValueTypePreTest() { const int count = 10000000; using (new OperationTimer("List<Int32>")) { List<int> l = new List<int>(); for (int i = 0; i < count; i++) { l.Add(i); int x = l[i]; } l = null;//确保进行垃圾回收 } using (new OperationTimer("ArraryList of Int32")) { ArrayList arr = new ArrayList(); for (int i = 0; i < count; i++) { arr.Add(i); int x = (int)arr[i]; } arr = null; } } private static void ReferenceTypePreTest() { const int count = 10000000; using (new OperationTimer("List<String>")) { List<string> l = new List<string>(); for (int i = 0; i < count; i++) { l.Add("X"); string x = l[i]; } l = null;//确保进行垃圾回收 } using (new OperationTimer("ArraryList of String")) { ArrayList arr = new ArrayList(); for (int i = 0; i < count; i++) { arr.Add("X"); string x = (string)arr[i]; } arr = null; } } } class OperationTimer : IDisposable { private long _start; private string _text; private int _collectionCount; public OperationTimer(string text) { _text = text; _collectionCount = GC.CollectionCount(0); _start = Stopwatch.GetTimestamp(); } public static void PreparForPeration() { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } public void Dispose() { Console.WriteLine("{0,6:###.00} seconds(GCs={1,3}) {2}", (Stopwatch.GetTimestamp() - _start) / (double)Stopwatch.Frequency, GC.CollectionCount(0) - _collectionCount, _text ); } }
Wintellect的Power Collections库
Wintellect公司开发的一些C++集合类库
泛型基础结构
开放类型和封闭类型
具有泛型类型参数的类型称为开放类型(例如:Directory<,>),CLR禁止构造开放类型的任何实例
代码引用一个泛型类型时,可指定一组泛型类型实参,假如为所有类型实参传递都是实际数据类型,称为封闭类型(例如:List<string>)开放类型和封闭类型
具有泛型类型参数的类型称为开放类型(例如:Directory<,>),CLR禁止构造开放类型的任何实例
代码引用一个泛型类型时,可指定一组泛型类型实参,假如为所有类型实参传递都是实际数据类型,称为封闭类型(例如:List<string>)泛型类型和继承
泛型类型任然是一种类型,它可以从其他类型派生。
泛型类型的同一性
不要为了简化代码而定义一个新的类型继承泛型,这样会散失同一性和相等性,可以通过下面的方式简化代码输入
using DateTimeList=System.Collections.Generic.List<DateTime>;
代码爆炸
CLR 优化了泛型类型的编译代码,避免了每次都要为不同的泛型类型生成对应的代码
泛型接口
通过泛型接口避免值类型发生装箱
public interface IEnumerator<T>:IDisposable,IEnumerator { T Current{get;} }
泛型委托
保证任何类型的对象都能以一种类型安全的方式传给回调方法;而且泛型委托也是为了避免一个值类型实例在传递给回调方法时不再发生装箱
委托和接口的协变和逆变泛型类型实参
泛型方法
泛型除了可以定义类型的参数,还可以为方法定义一个只作用域于方法的类型参数
泛型的类型推断
public static void Display<T>(T input){ System.Console.WriteLine(input); }
Display(123); Display("aaa")
泛型和其他成员
在C#中,属性、事件、索引器、构造函数等成员是不能有类型参数的,但是在泛型类型中,这些成员的代码是可以使用类型参数的;
C#不允许他们指定自己的泛型类型参数,C#团队认为开发人员很少需要将这些成员作为泛型使用,当然为这些成员添加泛型代价也很高可验证性和约束