声明
本文内容来自微软 MVP solenovex 的视频教程——真会C#? - 第3章 泛型简介(完结),大致和第 1 课—— 3.9 泛型为什么存在、类型参数、默认值等 对应。可在 GitHub 中查看 C# 视频教程的配套PPT
本文主要包括以下内容:
- 泛型类型
- 为什么泛型会出现
- 泛型方法
- 声明类型参数
- typeof 与未绑定的泛型类型
- 泛型的默认值
泛型
C# 语言和公共语言运行时 (CLR) 的 2.0 版本中添加了泛型。 泛型将类型参数的概念引入 .NET Framework,这样就可以设计具有以下特征的类和方法:在客户端代码声明并初始化这些类和方法之前,这些类和方法会延迟指定一个或多个类型。 例如,通过使用泛型类型参数 T,可以编写其他客户端代码能够使用的单个类,而不会产生运行时转换或装箱操作的成本或风险。
C# 泛型和 C++ 模板是类似的概念,但它们的工作方式不同。
泛型类型
泛型会声明类型参数 – 泛型的消费者需要提供类型参数(argument)来把占位符类型填充上。
public class Stack<T>
{
int position;
T[] data = new T[100];
public void Push (T obj) => data[position++] = obj;
public T Pop() => data[--position];
}
var stack = new Stack<int>();
stack.Push (5);
stack.Push (10);
int x = stack.Pop(); // x is 10
int y = stack.Pop(); // y is 5
public class ###
{
int position;
int[] data = new int[100];
public void Push (int obj) => data[position++] = obj;
public int Pop() => data[--position];
}
Stack<T> Open Type(开放类型),Stack<int> Closed Type(封闭类型),在运行时,所有的泛型类型实例都是封闭的(占位符类型已被填充了)。
var stack = new Stack<T>(); // Illegal: What is T?
public class Stack<T>
{
...
public Stack<T> Clone()
{
Stack<T> clone = new Stack<T>(); // Legal
...
}
}
为什么泛型会出现
public class ObjectStack
{
int position;
object[] data = new object[10];
public void Push (object obj) => data[position++] = obj;
public object Pop() => data[--position];
}
需要装箱和向下转换,这种转换在编译时无法进行检查。
// Suppose we just want to store integers here:
ObjectStack stack = new ObjectStack();
stack.Push ("s"); // Wrong type, but no error!
int i = (int)stack.Pop(); // Downcast - runtime error
泛型方法
泛型方法在方法的签名内也可以声明类型参数。
static void Swap<T> (ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 5;
int y = 10;
Swap (ref x, ref y);
Swap<int> (ref x, ref y);
在泛型类型里面的方法,除非也引入了类型参数(type parameters),否则是不会归为泛型方法的。只有类型和方法可以引入类型参数,属性、索引器、事件、字段、构造函数、操作符等都不可以声明类型参数。但是他们可以使用他们所在的泛型类型的类型参数。
public T this [int index] => data [index];
public Stack<T>() { } // Illegal
声明类型参数
在声明 class、struct、interface、delegate 的时候可以引入类型参数(Type parameters)。其它的例如属性,就不可以引入类型参数,但是可以使用类型参数。
public struct Nullable<T>
{
public T Value { get; }
}
泛型类型/泛型方法可以有多个类型参数:
class Dictionary<TKey, TValue> {...}
Dictionary<int,string> myDic = new Dictionary<int,string>();
var myDic = new Dictionary<int,string>();
声明泛型类型
泛型类型/泛型方法的名称可以被重载,条件是参数类型的个数不同:
class A {}
class A<T> {}
class A<T1,T2> {}
按约定,泛型类型/泛型方法如果只有一个类型参数,那么就叫 T。当使用多个类型参数的时候,每个类型参数都使用T作为前缀,随后跟着具有描述性的一个名字。
typeof 与未绑定的泛型类型
开放的泛型类型在编译后就变成了封闭的泛型类型。但是如果作为Type对象,那么未绑定的泛型类型在运行时是可以存在的。(只能通过 typeof 操作符来实现)
class A<T> {}
class A<T1,T2> {}
...
Type a1 = typeof (A<>); // Unbound type (notice no type arguments).
Type a2 = typeof (A<,>); // Use commas to indicate multiple type args.
Type a3 = typeof (A<int,int>);
class B<T> { void X() { Type t = typeof (T); } }
泛型的默认值
使用 default 关键字来获取泛型类型参数的默认值。
static void Zap<T> (T[] array)
{
for (int i = 0; i < array.Length; i++)
array[i] = default(T);
}
Generics
参考
Generics (C# Programming Guide)
Generic Methods (C# Programming Guide)
Generic Classes (C# Programming Guide)
网友评论