通过上周的学习,对泛型有一点点认识。其实泛型的功能是相当强大的,它涉及到了类、接口、方法、委托、数组、反射等等,从而让我对C#又有了一个全新的概念。
一、泛型方法
泛型方法是使用类型参数声明的方法。
1 |
static void Swap<T>( ref T lhs, ref T rhs) |
1 |
static void Main( string [] args) |
6 |
Swap< int >( ref a, ref b); |
7 |
Console.WriteLine(a + " " + b); |
这个调用和一般方法调用没什么太大的区别,此时也可以省略类型参数,编译器将推断出该参数。可以这样调用Swap(ref a, ref b)这样就和我们一般方法调用一样,但可以编译通过。类型推断规则无法仅从约束和返回值推荐类型参数,因此它不适用于没有参数的方法。
3 |
void Swap( ref T lhs, ref T rhs) { } |
在泛型类中,非泛型方法可以访问类级别类型参数。在上面的代码片段中,T允许被访问。
04 |
void SampleMethod<T>() { } |
10 |
void SampleMethod<U>() { } |
如果定义的泛型方法接受与包含类相同的类型参数,编译器会生产警告。这就相当于我们在处理异常时,如果要捕获多个异常,而只使用一个对象e就不会通过编译。
01 |
void SwapIfGreater<T>( ref T lhs, ref T rhs) where T : IComparable<T> |
04 |
if (lhs.CompareTo(rhs) > 0) |
使用约束对方法中的类型参数可以启用更专门的操作,泛型约束在后面会有讲述。
二、类型参数的约束
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。下表列出了六种类型的约束:
约束 说明
T:结构 类型参数必须是值类型。可以指定Nullable以外的任何值类型
T:类 类型参数必须是引用类型
T:new() 类型参数必须具有无参数的公共构造函数
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的
T:U 为T提供的类型参数必须是为U提供的参数或派生自为U提供的参数。这称为裸类型约束
没有约束的类型参数(如类SampleClass<T>{}中的T)称为未绑定的类型参数。它具有以下规则:
1)不能使用!=和==运算符,因为无法保证具体类型参数能支持这些运算符。
2)可以在它们与System.Object之间来回转换,或将它们显示转换为任何接口类型。
3)可以将它们与null进行比较。将未绑定的参数与null进行比较时,如果类型参数为值类型,则该比较将始终返回false。
三、泛型和数组
05 |
int [] arr = { 0, 1, 2, 3, 4 }; |
06 |
List< int > list = new List< int >(); |
08 |
for ( int x = 5; x < 10; x++) |
13 |
ProcessItems< int >(arr); |
14 |
ProcessItems< int >(list); |
17 |
static void ProcessItems<T>(IList<T> coll) |
19 |
foreach (T item in coll) |
21 |
Console.Write(item.ToString() + " " ); |
这个小示例使用到了泛型方法、泛型参数,这使我们能够使用相同代码循环访问数组和其他集合类型。
后面还涉及到泛型接口、泛型委托以及与反射之间的完美结合,由于涉及到的知识面比较广,用一篇文章是远远写不完的。我觉得还是需要首先对接口、委托、反射这些基础知识有了扎实的理解后,再去学习这方面的内容比较好上手一些。如果有朋友可以分享这方面的所学所得,小弟不甚感激。