泛型的理解

.NET FrameWork 2.0的语法 。

【应用】对于不同类型的参数,有着相同或类似的行为逻辑时,可以考虑使用泛型重用代码。

【原理】延迟声明,方法没有指定参数类型,而是在调用的时候指定。在编译的时候,类型参数编译为占位符,程序运行的时候,JIT及时编译替换为真实类型。

【扩展】泛型类、泛型委托、泛型方法、泛型接口。

普通类不能继承泛型类,必须指定泛型类型参数后才可以。

【泛型约束的五大类型,当然也可以多重约束,但是要注意约束直接的关系是否有嵌套】

public  static T Get<T>()  where T : new() //无参数构造函数约束

            where T: class//引用类型约束

               where T:struct //值类型约束

            where T:Interface//接口约束

where T:People //基类约束(People在这里是一个class)

{

  return default(T);//返回泛型类型的默认值,引用类型的默认值是null,值类型不是

}

【协变、逆变】

只能放在接口或者委托的泛型参数前面。

out 协变(covariant) 修饰返回值   c#中的IEnumerable泛型接口;

in   逆变(contravariant) 修饰传入参数  c#中的Action泛型委托;

public class Dog{} //狗的父类

public class Labrador:Dog{}//子类 拉布拉多

在继承关系上,父类出现的地方都可以用子类代替,因此下面的实例化都是可以的:

{

  Dog dog = new Dog();

  Dog dog = new Labrador();

}

{

  List<Dog> dogs = new List<Dog>();// 语法是肯定没问题的

  List<Dog> dogs = new  List<Labrador>(); //语法是出错的

  List<Dog>  dogs = new List<Labrador>().Select(d=>(Dog)d).ToList(); //语法是通过的

}

上述的第二个方法块内的第二个实例化为什么会出错呢?讲道理拉布拉多是继承自狗的父类,一群拉布拉多也是一群狗,应该不会出错的。

虽然拉布拉多和狗是父子关系,但是List<Dog>和List<Labrador>不是父子关系,因此不能实例化。在c# 4.0的语法中出现协变和逆变的概念,允许我们这样操作,可以理解成是为了简化我们代码的操作

IEnumerable<out T> 是一个泛型接口,List继承这一泛型接口,out其实就是在告诉编译器,这里我们接受这样的一个自动转换

{//协变out,只能作为返回结果,不能作为传入

  IEnumerable<Dog> dog = new List<Dog>();

  IEnumerable<Dog> dog = new List<Labrador>();//这一次语法是通过的

}

{//逆变in,不能作为返回值,只能作为参数传入

  IDogs<Labrador> dog = new List<Labrador>();

  IDogs<Labrador> dog = new List<Dog>();//这一次语法是通过的

  public interface IDogs<int T>{}

}

在实际的代码中我们可能并不需要自己去定义这样的协变和逆变,大部分是框架为我们定义好的,比如Func<string,int> string就是一个逆变,int就是一个逆变参数。

【泛型缓存】

 在程序中一般用static做缓存,多以Dictionary来缓存数据。

然而泛型是程序在运行的时候,JIT及时编译替换为真实类型。

public class GenericCache<T> //泛型类

{

  static GenericCache()

  {

    time = string.Format("{0}_{1}",typeof(T).FullName,DataTime.Now.ToString("yyyyMMddHHmmss.fff"));

  }

  private static string time = "";

  public static string GetCatch()

  {

    return time;

  }

}

//主程序调用

for(int i=0;i<5;i++)

{

  GenericCache<int>.GetCatch();

  GenericCache<string>.GetCatch();

  GenericCache<DateTime>.GetCatch();

}

第一次循环进来,该类的构造函数会执行三次,静态的值也会被初始化三次;第二次循环进来,无需走构造函数,直接返回缓存值。

优缺点以及应用场景:

(1)速度快,第二次访问的时候不需要再堆栈中寻址,而是在CPU旁边直接访问;

(2)不同的类型只有一个值的时候可以应用

(3)缺点:缓存的清除麻烦

posted @ 2018-10-01 16:51  这个年纪的人  阅读(127)  评论(0)    收藏  举报