1.作用
代码的复用
最开始是使用object类型,但是涉及到装拆箱,导致性能变差。
2.泛型类型
泛型会声明类型参数–生产类型,消费者提供类型参数(argument)来把占位符类型填充上
public class Stack<T>
{
T a;
return a ;
}
var stack =new Stack<int>();//合成发生在运行时。
编译的时候
stack = new string();会报错
Stack 开发类型
Stack封闭类型
运行时,泛型类型实例都是封闭的。但可以使用Typeof来使泛型在编译的时候保持开放。
3.泛型方法(必须引用了,在泛型类型里面,使用(T a )的时候是普通方法)
class TClassDemo<T>
{
public void factionName(T a){...}//普通方法
}
void factionName<T> (T a)//泛型方法
{
...
}
调用的时候根据传进的参数
4.允许引入类型参数的只有类型(class struct interface delegate)和方法
5.泛型类型/泛型方法的名称可以被重载
class A
class A<T>
class A<T1,T2>
以上名称相同的class的存在并不冲突。
另外按照约定,只有一个类型参数的时候一般使用T。
6.type对象允许未绑定类型的泛型类型在运行时存在
class A<T>{}
class A<T1,T2>
Type a1=typeof(A<>)
Type a2=typeof(A<,>)
与反射使用
7.泛型类型参数的默认值
使用default关键字。值类型按位归0的值,应用类型为null。
8.泛型约束
裸类型约束,不指定约束,指定约束 where T
where T:base-class //继承了自某个父类
where T:interface //实现了某个接口
where T:class //引用类型
where T:struct //值类型
where T:new() //有一个无参的构造函数
where U:T //U继承对于T的约束
9.泛型类的子类
在子类里可以让类型参数保持开放
class Stack<T> {...}
class SpecialStack<T>:Stack<T> {...}
也可以关闭
class SpecialStack:Stack<int>{...}
也可以加入新的类型参数
class List<T> {...}
class KeyedList<TElement,TKey>:List<TElement>
子类继承父类的时候可以这样理解会先关闭类型参数,然后重新打开,为类型参数带来新的名称或者含义,如classKeyedList<TElement,TKey>
10.自引用的泛型声明
public interface IEquatable<T>{...}
public class Ballon:IEquateble<Ballon>
可以这样使用,但是不常用
11.针对每一个封闭类型,静态数据是唯一的
class Bod<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<bool>.Count);//1
}
}
12.泛型的转换
当不确定类型参数的时候,直接转换的时候会报错。
转换的过程涉及到协变,逆变。(类型参数关闭后)
协变,逆变是先对于变量而言。泛型变量关闭后,和普通的变量并没有区别,一个变量要么是支持协变,要么是支持逆变,要么是不变的。
协变的类型转换过程是从子类到父类的过程,支持隐式转换。在使用泛型过程中,如果使用out关键字声明泛型的类型参数,那么该类型只能作为返回值类型使用。
逆变的类型转换过程是从父类到子类的过程,需要显示的转换,或者使用as操作符,在使用泛型过程中,如果泛型的类型参数,用in 关键字声明,则在使用过程不用显示的转换,也能够完成从父类到子类的转换
不变的类型转换过程是需要显示的转换,或者使用as操作符,且在使用泛型过程中,不使用out,in关键字来标明
(1)如IList 这个类型参数在声明的时候,并没有使用out或者in的关键字。使用过程如果是使用了隐式的类型转换则会报错。
IList<string> strings =new List<string>{"a" ,"b","c"};
IList<object> objects =strings;//报错,无法隐式的转换
如果想用IList类型的参数,来使用的话,可以使用strings.ToList();或者strings.Cast().ToList();来得到List类型的变量的返回值。注意,得到返回值类型就是object类型,把返回值赋过去的过程是不涉及类型转换的。
IList<string> list1 = new List<string> { "a", "b", "c" };
IList<object> list2 = list1.ToList<object>();
(2)IEnumerable 这个类型参数T 的声明是public interface IEnumerable : IEnumerable out T的,是支持从子类到父类的协变的,string为object的子类,所以是支持隐式转换的
IEnumerable<string> strings = new List<string> { "a", "b", "c" };
IEnumerable<object> objs = strings;
(3)Action这个类型的参数的声明是public delegate void Action in T的,支持从父类到子类的转换,object为string的父类,actionObject转换成actionString的过程是逆变。一般的object转为string的过程是需要显示类似转换或者as操作符的,但是当类型参数T,用了in关键字说明,这其中的转换就不需要使用了,但是用这中类型参数声明的变量,只能作为输入值使用。
Action<object> actionObject;
Action<string> actionString;
actionString = actionObject;
即out T 引入类型参数的时候,T的参数只能作为返回,允许协变子类变为父类,Covariance,协变,子类转为父类,或者本体转换的隐式转换方式。
class Testout<out T>
{
public T returnOneT (int a,int b)
{
return T;
}
}
in T引入类型参数的时候,T只能作为输入,允许父类转为子类,Contravariance,逆变,一般不被允许,需要显示转换的过程。
class Testout<inT>
{
public void returnOneT (T a,T b)
{
}
}
可以这样理解记忆:作为返回的值的时候,最主要的是得到数据,而不是对数据做处理,故而父类的类型就够用。作为输入使用的时候,希望实现的越多越好,故而需要子类的类型。
没有修饰符的,作为invariance。封闭后的类型转换是不安全的。故而需要的是显示类型转换或者是as操作