泛型的作用
跨类型可复用的代码:继承 和 泛型。
其中继承通过基类来代表代码的复用性,而泛型使用占位符(类型的占位符)
继承 → 基类
泛型 → 带有“(类型)占位符”的”模板“
泛型的类型(GENERIC TYPES)
泛型会声明类型参数 - 泛型的消费者需要提供类型参数(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(); // x is 5
// 相当于下面
public class ###
{
int position;
int[] data = new int[100];
public void Push(int obj) =>data[position++] = obj;
public int Pop() => data[--position];
}
OPEN TYPE & CLOSED TYPE
Stack
Stack
在运行时,所有的泛型类型实例都是封闭的(占位符类型已经被填充了)
var stack = new Stack<T>(); //不合法的,因为占位符类型没有被填充
public class Stack<T> //合法的
{
..
public Stack<T> Clone()
{
Stack<T> clone = new Stack<T>();
//..
}
}
为什么泛型会出现
public class ObjectStack
{
int position;
object[] data = new object[100];
public void Push(int obj) =>data[position++] = obj;
public object Pop() => data[--position];
}
为了可以写出适应不同类型的代码,如果没有
需要装箱和向下转换,这种转换在编译时无法进行检查
satck.Push("s";) //不同的类型,但是没有报错
int i = (int)stack.Pop(); //向下装换 运行时错误,因为他不是int类型
泛型方法
泛型方法在方法签名内也可以声明类型参数
static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
static void Main(string[] args)
{
int x = 5;
int y = 10;
Swap(ref x, ref y);
Console.WriteLine($"{x},{y}"); // Input 10,5
}
如果发生歧义,不知道类型的类型的话可以swap
(ref x, ref y)这样写 - 在方形类型里面的方法,除非也引入了类型参数(type parameters),否则是不会归为泛型方法的。
只有类型和方法可以引入类型参数,属性、索引器、实践、字段、构造函数、操作符等等都不可以声明类型参数。但是他们可以使用他们所在的泛型类型的类型参数。public class Stack<T>
{
int position;
T[] data = new T[100];
public void Push(T obj) => data[position++] = obj;
//这引用的T,是在泛型类型那声明的,而不是方法引入的,就是没有是用<>来引入其它的类型参数(占位符),所以说这个Pop()方法它不是泛型方法,只不过是泛型类型里面的一个普通方法
public T Pop() => data[--position];
}
public T this[int index] => data[index];
public Stack<T>() {} //构造函数引入一个类型参数是不合理的
声明类型参数
在声明class、struct、internal、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<>) //联合类型,注意没有类型参数
Type a2 = Typeof(A<,>) //使用逗号表示多个类型参数
开放的泛型类型,通常与反射的API一起来使用的
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);
}