泛型 (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; }}
阅读全文
0 0
- 泛型 (2017-10-31)
- 2017-10-31
- 2017-10-31
- 2017-10-31
- 2017-10-31
- 2017-10-31
- 2017-10-31课堂作业
- 2017-10-31每日练习
- 2017-10-31课堂作业
- 每日练习2017-10-31
- 2017-10-30 & 2017-10-31 集训总结
- [2017纪中10-31]Tree 构造
- [2017纪中10-31]Calculate 数论
- 2017-10-31课堂作业01
- 2017-10-31课堂作业02
- 2017年10月31日作业
- 2017-10-31每日一练
- 2017-10-31课堂作业1
- 指针与函数_函数指针转换
- 数据库的认证与证书
- (实验3-2 )构造函数的调用
- java.util.base64
- POJ:3461-Oulipo(KMP模板题)
- 泛型 (2017-10-31)
- 机器学习高斯混合模型(中篇):聚类求解
- 前端框架与库的区别
- EventBus事件总线框架的使用
- python基础篇之python中的数据类型
- 外部中断边沿触发以及电平触发区别
- 云星数据---Scala实战系列(精品版)】:Scala入门教程060-Scala实战源码-Scala package 包对象
- 关于内存溢出的原因分析及解决方案探讨
- linux中的mail命令