泛型类型 Generic types
泛型会声明类型参数—泛型的消费者需要提供类型参数来把占位符类型填充
public class Stack<T>
{
int positon;
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(2);
stack.Push(3);
int x=stack.Pop();//2
int y=stack.Pop();//3
open type & closed type
Stack Open Type(开放类型)
Stack Closed Type(封闭类型)
在运行时,所有的泛型类型实例都是封闭的(占位符已被填充了)
为什么会出现泛型
没有出现泛型之前,不同的类型实现相同的功能会写一些重复的代码。
为了解决这一点,通常会用object类型来实现一些通用的功能(因为所有的引用类型都继承自object)。但是使用object类型时,有时候会发生一些类型转化,出现装箱、拆箱的操作,损失性能,因此泛型应运而生
public class ObjectStack{
int position;
object[] data=new object[10];
public void Push(object obj)=>data[position++]=obj;
publish void Pop()=>data[--position];
}
需要装箱和向下转换,这种转化在编译时无法进行检查:
stack.Push("s");//不是object类型但可以Push进入
int i=(int)Stack.Pop();//读取时类型转换错误,编译时不会提示
stack.Push(20);//装箱操作,将值类型转化成引用类型
var value=(int)Stack.Pop();//拆箱操作,将引用类型转化为值类型
泛型方法
static void Swap<T>(ref T a,ref T b){
T temp=a;
a=b;
b=temp;
}
int x = 5;
int y = 10;
Swap<int>(ref x, ref y);
//类型参数也可以省略,编译器可根据传入的参数隐式推断出类型
//也可以写成:
Swap(ref x,ref y);
在泛型类型里面的方法,除非也引入了类型参数,否则是不会归为泛型方法,即方法名后必须跟<T,…>才是泛型方法
//不是泛型方法,尽管参数有泛型
public void GetValue(T entity){ }
//是泛型方法
public void GetValue<T>(T entity){ }
只有类型和方法可以引入类型参数,属性、索引器、事件、字段、构造函数、操作符等不可以声明类型参数,但是他们可以使用他们所在的泛型类型的类型参数
public T this[int index] => data[index];//合法
public Stack<T>(){ }//构造函数不能引入类型参数,不合法
声明类型参数
- 在声明==class、struct、inteface、delegate的时候可以引入类型参数
- 其它的例如属性,就不可以引入类型参数,但是可以使用类型参数
public struct Nullable<T>
{
public T Value{get;}
}
3.泛型类型/泛型方法可以有多个类型参数:
class Dictionary<TKey,TValue>{ }
var myDic=new Dictionary<int,string>();
4.泛型类型/泛型方法的名称可以被重载,条件是参数类型的个数不同:
class A { }
class A<T> { }
class A<T1,T2>{ }
按约定,泛型类型/泛型方法如果只有一个类型参数,那么就叫T。
当使用多个类型参数的时候,每个类型参数都是用T作为前缀,随后跟着具有一个描述性的一个名字。
泛型的默认值
使用default关键字来获取泛型类型参数的默认值
//T是引用类型,默认值是null
//T是值类型,默认值是0
static void Zap<T>(T[] array){
for(int i=0;i<array.Length;i++)
{
array[i]=default(T);
}
}
泛型的约束
默认情况下,泛型的类型参数可以是任何类型
如果只允许使用特定的参数类型,就可以指定约束
where T:base0-class //某个父类的子类
where T:interface //实现了某个接口
where T:class //T必须是引用类型
where T:struct //T必须是值类型
where T:new() //T必须有一个无参的构造函数
where U:T //某个类必须继承T
class SomeClass{}
interface Interface1{}
class GenericClass<T,U> where T:SomeClass,Interface1
where U:new()
{ }
泛型约束可以作用域类型或方法的定义
public interface Icomparable<T>
{
int CompareTo(T other);
}
//泛型约束作用于方法
static T Max<T>(T a,T b) where T:IComparable<T>
{
return a.CompareTo(b) > 0 ? a:b;
}
struct Nullable<T> where T:struct { ... }
static void Initialize<T>(T[] array) where T:new()
{
for(int i=0;i<array.Length;i++)
{
array[i]=new T();
}
}
//裸类型约束
class Stack<T>
{
Stack<U> FilteredStack<U>() where U:T{...}
}
泛型类型的子类
泛型类型class 可以有子类,在子类里,可以继续让父类的类型参数保持开放
class Stack<T> {...}
class SpecialStack<T>:Stack<T>{...}
在子类里,也可以使用具体的类型来关闭(封闭)父类的类型参数
class intStack:Stack<int>{...}
子类型也可以引入新的类型参数:
class List<T> {...}
class keyedList<T,TKey>:List<T> {...}
技术上讲,所有子类的类型参数都是新鲜的。你可以认为子类先把父类的类型参数给关闭了,然后又打开了。为这个先关闭后打开的类型参数带来新的名称或含义
class List<T> {...}
class KeyedList<TElement,TKey>:List<TElement>{...}
自引用的泛型声明
在封闭类型参数的时候,该类型可以把它自己作为具体的类型
public interface IEquatable<T> { bool Equals(T obj);}
public class Balloon:IEquatable<Balloon>
{
public string Color{get;set;}
public int CC {get;set;}
public bool Equals(Balloon b)
{
if(b==null) return false;
return b.Color==Color&& b.CC==CC;
}
}
静态数据
针对每一个封闭类型,静态数据是唯一的
class Bob<T>
{
public static int Count;
}
class Test
{
static void Main()
{
Console.WriteLine(++Bob<int>.Count); //1
Console.WriteLine(++Bob<int>.COunt);//2
Console.WriteLine(++Bob<string>.Count);//1
Console.WriteLine(++Bob<object>.Count);//1
}
}