泛型 (2017-10-31)

来源:互联网 发布:石材效果图制作软件 编辑:程序博客网 时间:2024/06/06 01:24

1、泛型的引用:
object类型是任意类型的父类,子类拥有父类的一切属性和行为,任何父类出现的地方都可以用子类来代替

//.NET Framework 1.0 1.1时代public static void ShowObject(object oParameter){    Console.WriteLine($"This is {typeof(CommonMethod)},parameter={oParameter.GetType().Name},type={oParameter}");}

2、泛型的概念

  • 泛型类型:
List<T>泛型类集合,如List<int>List<string>
  • 泛型方法:
public static void Show<T>(T tParameter){    Console.WriteLine($"This is {typeof(GenericMethod)},parameter={tParameter.GetType().Name},type={tParameter.ToString()}");}
  • 泛型方法调用
Show<int>(2);Show<string>("This is string");
  • 调用泛型方法时,其中的类型参数,可以由VS根据输入参数的类型自动推算,so 可以直接写成 Show(“This is string”); 或者 Show(2);
  • 泛型方法在申明时,没有指定参数的具体类型,只是标明参数类型的占位符T,具体类型延迟到调用时由CLR/JIT即时编译,确定具体的类型。
  • 泛型方法与普通方法在性能上差不多,也就是说生成的可执行的指令(机器码)是相同的。
  • 测试普通方法和泛型方法的性能:
int iValue = 12345;long commonSecond = 0;long objectSecond = 0;long genericSecond = 0;//普通方法{   Stopwatch watch = new Stopwatch();   watch.Start();   for (int i = 0; i < 100000000; i++)   {       ShowInt(iValue);   }   watch.Stop();   commonSecond = watch.ElapsedMilliseconds;}//object方法{   Stopwatch watch = new Stopwatch();   watch.Start();   for (int i = 0; i < 100000000; i++)   {      ShowObject(iValue);   }   watch.Stop();   objectSecond = watch.ElapsedMilliseconds;}//泛型方法{   Stopwatch watch = new Stopwatch();   watch.Start();   for (int i = 0; i < 100000000; i++)   {       Show<int>(iValue);   }   watch.Stop();   genericSecond = watch.ElapsedMilliseconds;}   Console.WriteLine($"commonSecond={commonSecond},objectSecond={objectSecond},genericSecond={genericSecond}");}

3、泛型应用:
泛型主要解决:不同类型,相同代码的重用
泛型主要用途:泛型类、泛型方法、泛型接口、泛型委托
例如,泛型类代码如下:

//泛型类声明public class GenericClass<T>{    public T Property{get;set;}}
//泛型类实例化GenericClass<int> iGenericClass = new GenericClass<int>();iGenericClass.Property = 1;GenericClass<string> iGenericClass = new GenericClass<string>();iGenericClass.Property = "123";
//泛型类的继承//已指定T的类型为int,ChildClass是普通类public class ChildClass : GenericClass<int>////ChildClass<T>类是泛型子类, 需要指定T占位符public class ChildClass<T>:GenericClass<T>

4、泛型约束
五种泛型约束类型:基类约束、接口约束、引用类型约束、值类型约束、无参构造函数约束

  • 基类约束:在泛型类申明后使用where BaseClass进行约束,例如
public static void Show<T>(T tParameter) where T:People{    //where后跟上占位符T : People,就是说T类型只能是跟People类有关的类型}
public static T DoNothing<T>(T tParameter)    //where T : ISports//接口约束    //where T : class//引用类型约束    //where T : struct//值类型约束    where T : new()//无参数构造函数约束{    //tParameter.Pingpang();    //return null;    T t = new T();    return default(T);//会根据T的类型,去产生一个默认值}

注意:泛型基类约束,可以与接口约束等其它约束迭加,比直接指定基类参数更灵活,试比较以下两段代码:

 public static void Show<T>(T tParameter)    where T : People, ISports, new()//都是and 关系{    Console.WriteLine("This is {0},parameter={1},type={2}",typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());    Console.WriteLine(tParameter.Id);    Console.WriteLine(tParameter.Name);    tParameter.Hi();    tParameter.Pingpang();    T t = new T();}
public static void ShowPeople(People tParameter){   Console.WriteLine("This is {0},parameter={1},type={2}",typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());   Console.WriteLine(tParameter.Id);   Console.WriteLine(tParameter.Name);   tParameter.Hi();   //tParameter.Pingpang();}

5、协变和逆变
协变和逆变主要解决输入参数和返回参数的类型自动转换的问题
例如:Bird基类和子类Sparrow

public class Bird{    public int Id{get;set;}}
public class Sparrow:Bird{    public string Name{get;set}}

以下代码是合理的:

Bird bird1 = new Bird();Bird bird2 = new Sparrow();//左边是父类,右边是子类Sparrow sparrow1 = new Sparrow();//Sparrow sparrow2 = new Bird();//不是所有的鸟都是麻雀

但在泛型集合中的成员却无法像上面代码那样自动转换,需要手动进行转换:

List<Bird> birdList1 = new List<Bird>();//这是一群鸟//List<Bird> birdList2 = new List<Sparrow>();//这种写法是错误的。它们之间没有父子关系List<Bird> birdList3 = new List<Sparrow>().Select(s => (Bird)s).ToList();//把所有的元素都转换一遍就不会出错了。//问题出现了:每一个元素都需要去转换

协变:在泛型参数T前加上out关键字,只能用作返回值,例如框架中的IEnumerable<out T>接口

//协变:接口泛型参数加了个out,就是为了解决刚刚上面代码的不和谐IEnumerable<Bird> birdList1 = new List<Bird>();IEnumerable<Bird> birdList2 = new List<Sparrow>();//Func<Bird> func = new Func<Sparrow>(() => null);ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();ICustomerListOut<Bird> customerList2 = new CustomerListOut<Sparrow>();

上面的ICustomerListOut接口是这样定义的:

public interface ICustomerListOut<out T>{   T Get();   //void Show(T t);//不能做参数}

逆变:在泛型参数T前加 In 关键字,只能用作传入参数

/// <summary>/// 逆变:只能修饰传入参数/// </summary>/// <typeparam name="T"></typeparam>public interface ICustomerListIn<in T>{   //T Get();   void Show(T t);}

以下是逆变的调用代码:

//逆变ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Sparrow>();ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>();//customerList1.Show()ICustomerListIn<Bird> birdList1 = new CustomerListIn<Bird>();birdList1.Show(new Sparrow());birdList1.Show(new Bird());Action<Sparrow> act = new Action<Bird>((Bird i) => { });

协变和逆变的应用

public interface IMyList<in inT, out outT>{   void Show(inT t);   outT Get();   outT Do(inT t);   ////out 只能是返回值   in只能是参数   //void Show1(outT t);   //inT Get1();}

6、泛型类的应用
泛型静态缓存性能比普通的字典缓存性能要高很多
对于每一个普通方法来说他的静态字段,静态构造构造函数,只在该类初始化时执行一次,并且常驻内存中。
泛型类在使用不同类型的泛型参数进行初始化时,会产生与该类关联的独立的静态字段,并且每次指定不同类型进行初始化时,都会执行一次该类型对应的静态构造函数,利用此特性可以做成性能很高的泛型缓存

/// <summary>/// 每个不同的T,都会生成一份不同的副本/// 适合不同类型,需要缓存一份数据的场景,效率高/// </summary>/// <typeparam name="T"></typeparam>public class GenericCache<T>{    static GenericCache()    {        Console.WriteLine("This is GenericCache 静态构造函数");        _TypeTime = string.Format("{0}_{1}",typeof(T).FullName,DateTime.Now.ToString("yyyyMMddHHmmss.fff"));    }    private static string _TypeTime = "";    public static string GetCache()    {        return _TypeTime;    }}
原创粉丝点击