深入理解C#(九)
*第三章(C# 2:泛型实现参数化类型)
理解泛型类型和方法
泛型方法的类型推断
例子:
1 | static List<T> MakeList<T>(T first,T second) |
使用编译器的类型推断:
1 | List<string> list=MakeList("Line 1","Line 2"); |
类型约束
如List
用约束来实现。约束放在泛型方法或泛型类型声明的末尾,有上下文关键字where引入。
- 引用类型约束
确保使用的类型实参是引用类型:必须是第一个约束struct RefSample<T> where T:class
以这种方式约束一个类型参数后,可以使用==和!=来比较引用(包括null) - 值类型约束
确保使用的类型实参是值类型:class ValSample<T> where T:struct
br>
设定值类型约束后,不允许使用==和!=进行比较。
以上两种约束比较少用,后两者更有用。 - 构造函数类型约束
检查类型实参是否有一个可用于创建类型实力的无参构造函数。必须是最后一个约束。public T CreateInstance() where T : new() { return new T(); }
在使用工厂风格的设计模式时,这个约束非常有用。 - 转换类型约束
指定另一个类型,类型实参必须可以通过一致性、引用或装箱隐式地转换为该类型。还可以规定一个类型实参必须可以转换为另一个类型实参。
这个约束意味着“在类型参数的实例上使用指定类型的成员”
实现泛型
假装T是一个真正的类型名称。额外要注意:
默认值表达式:
例子:Dictionary<TKey,TValue>有一个TryGetValue方法:用一个输出参数来接受你打算获取的值,用boolean返回值显示是否成功。这意味着方法必须用TValue类型的值来填充输出参数。
C# 2提供了默认值表达式(default value expression)。
例子:以泛型方式将一个给定的值和默认值进行比较class DefaultValueComparison
{
static int CompareToDefault(T value)
where T : IComparable
{
return value.CompareTo(default(T));
}1
2
3
4
5
6
7
8static void Main()
{
Console.WriteLine(CompareToDefault("x"));
Console.WriteLine(CompareToDefault(10));
Console.WriteLine(CompareToDefault(0));
Console.WriteLine(CompareToDefault(-10));
Console.WriteLine(CompareToDefault(DateTime.MinValue));
}}
这个泛型方法规定了只能使用实现了IComparable
类型推断只能用于泛型方法,有一个泛型类型,其中不包含任何泛型方法,怎么实现类型推断?Pair<int,string> pair=new Pair<int,string>(10,"value");
解决方法是使用包含泛型方法的非泛型辅助类。
1 | public static class Pair |
等到第七章再回看
- 直接比较
如果一个类型参数是未约束的(即没有对其应用约束),那么只能在该类型的值与null比较时才能使用==和!=操作符。不能直接比较两个T类型的值。如果一个类型参数被约束成值类型,就完全不能为它使用==和!=。如果被约束成引用类型,具体比较将完全取决于类型参数被约束成什么类型。
高级泛型
静态字段和静态构造函数
每个封闭类型都有它自己的静态字段集
同样的规则也适用于静态初始化程序和静态构造函数。
JIT(just in time即时)编译器如何处理泛型
暂略
泛型迭代
对集合执行的最常见操作之一是便利(迭代)所有元素。最简单的办法使用foreach语句。
当需要为自己的某个类型实现迭代时,由于IEnumerable
IEnumerator
IEnumerator GetRnumerator();
反射和泛型
反射的一切都是围绕“检查对象及其类型”展开的。
typeof可通过两种方式作用于泛型类型。一种方式是获取泛型类型定义,另一种方式是获取特定的已构造类型。
typeof(Dictionary<,>)或typeof(Dictionary<string,X>)
获取泛型和已构造Type对象的各种方式
1 | string listTypeName = "System.Collections.Generic.List`1"; |
反射泛型方法
1 | public static void PrintTypeParameter<T>() |
- 获取泛型方法定义
- 使用MakeGenericMethod返回一个已构造的泛型方法。
- 后去已构造的方法后,就可以调用了。
泛型在C#和其他语言中的一些限制
为什么不能将List
使用泛型辅助类解决逆变性缺乏问题
缺乏操作符约束或者数值约束
解决方法:
- 表达式树,第九章
- C# 4的动态特性,14章有例子
为什么泛型只限于类型(包括类、结构、委托和接口)和方法?
- 缺乏泛型属性、索引器和其他成员类型
第三章小结
泛型的三个优点:
- 编译时的类型安全性
- 性能
- 代码的表现力
IDE和编译器能提前验证代码
值类型性能上获益最大。在强类型的泛型API中,不再需要装箱和拆箱。
使用泛型,代码能更清楚地表达其意图。