menu 澹台听雨|Kamasylvia
more_vert

欢迎来到时空管理局银河系分局猎户座悬臂太阳系分站驻第三行星办事处。

[真会C#?] - 事件
[真会C#?] - 委托
[OneDrive] 免费获得 5T - 25T OneDrive 空间
[真会C#?] Type - 结构体
[真会C#?] Type - 枚举
[真会C#?] Type - 类型
[真会C#?] - 泛型

文档资料

泛型为什么存在、类型参数、默认值等

泛型的作用

  • 跨类型可复用的代码:继承和泛型。
    • 继承 -> 基类。
    • 泛型 -> 带有“(类型)占位符”的“模板”。

泛型类型

  • 泛型会声明类型参数 - 泛型的消费者需要提供类型参数(arguments)来把占位符类型填充上。
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
  • Stack<T>: Open Type 开放类型。
  • Stack<int>: Closed Type 封闭类型。
  • 在运行时,所有的泛型类型实例都是封闭的(占位符类型已被填充了)
public class ###
{
	int position;
	int[] data = new int[100];
	public void Push(int obj) => data[position++] == obj;
	public int Pop() => data[--position];
}
var stack = new Stack<T>(); // 不合法,T 要指明具体类型。

public class Stack<T>
{
	public Stack<T> Clone()
	{
		Stack<T> clone = new Stack<T>();	// 合法,在泛型类型里面可以使用 T 来代表类型。
	}
}

为什么泛型会出现

不使用泛型实现 Stack

public class ObjectStack
{
	int position;
	object[] data = new object[10];
	public void Push(object obj) => data[position++] = obj;
	public object Pop() => data[--position];
}
  • 需要装箱和向下转换,这种转换在编译时无法进行检查。
// We want int
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);
  • 在泛型类型里面的方法,除非也引入(使用了<T>)了类型参数(type parameters),否则是不会归为泛型方法的。
public class Stack<T>
{
	int position;
	T[] data = new T[100];
	public void Push(T obj) => data[position++] = obj;
	
	// Pop() 不是泛型方法,因为类型参数没有 T,只是一个泛型类型里面的普通方法。
	public T Pop() => data[--position];
}
  • 只有类型和方法可以引入(使用了<T>)类型参数,属性、索引器、事件、字段、构造函数、操作符等都不可以声明类型参数,但是他们可以使用他们所在的泛型类型的类型参数。
public T this[int index] => data[index];	// 合法,没有 <T>

public Stack<T>() {}	// 不合法,构造函数不能引入类型参数

声明类型参数

  • 在声明 classstructinterfacedelegate 的时候可以引入(使用了<T>)类型参数(Type parameters)。
  • 其他的例如属性,就不可以引入(使用了<T>)类型参数,但是可以使用类型参数。
public struct Nullable<T>
{
	public T Value { get; }
}
  • 泛型类型和泛型方法可以有多个类型参数。
class Dictionary<TKey, TValue> {...}

var myDic = new Dictionary<int, string>();
  • 泛型类型和泛型方法的名称可以被重载,条件是参数类型的个数不同。
class A {}
class A<T> {}
class A<T1, T2> {}
  • 按约定,泛型类型和花园泛型方法,如果只有一个参数类型,那么就叫 T
  • 当使用多个类型参数的时候,每个类型参数都使用 T 作为前缀,随后跟着具有描述性的一个名字,例如 <Tkey, TValue>

typeof 与未绑定的泛型类型

  • 开放的泛型类型在编译后就变成了封闭的泛型类型。
  • 但是如果作为 Type 对象,那么未绑定的泛型类型在运行时是可以存在的。(只能通过 typeof 操作符来实现)
class A<T> {}
class A<T1, T2> {}
...
Type a1 = typeof(A<>);	// Unbound type (notice no T)
Type a2 = typeof(A<,>);	// Use commas to indicate multiple type args.

泛型的默认值

  • 使用 default 关键字来获取泛型类型参数的默认值。
static void Zap<T>(T[] array)
{
	for (int i = 0; i < array.Length; i++)
		array[i] = default(T);	// 引用类型默认值为 null
}
[真会C#?] OOP - 接口
[真会C#?] OOP - 访问修饰符
[真会C#?] OOP - 继承和多态
召唤伊斯特瓦尔