.net基础—泛型
泛型
泛型在 .NET Framework 2.0 中首次引入,它本质上是一个“代码模板”,可让开发者定义类型安全数据结构,无需处理实际数据类型。 例如,List<T> 是一个可以声明的泛型集合,可与 List<int>、List<string> 或 List<Person> 等任何类型结合使用。
为方便理解泛型的作用,让我们看看添加泛型之前和之后的一个特定类:ArrayList。 在 .NET Framework 1.0 中,ArrayList 元素属于 Object 类型。 添加到集合的任何元素都会以静默方式转换为 Object。 从列表读取元素时,会发生相同的情况。 此过程称为装箱和取消装箱,它会影响性能。 但除了性能之外,在编译时无法确定列表中的数据的类型,这会形成一些脆弱的代码。 泛型解决了此问题,它可以定义每个列表实例将要包含的数据类型。 例如,只能将整数添加到 List<int>,只能将人员添加到 List<Person>。
泛型还可以在运行时使用。 运行时知道你要使用的数据结构类型,并可以更高效地将数据结构存储在内存中。
泛型的性能
通过执行一亿次三种不同类型方法:确定参数类型的方法、Object参数类型的方法、 泛型参数类型的方法,来看一下它们的耗时情况。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int testVal = 123; 6 long commonSecond = 0; 7 long objectSecond = 0; 8 long genericSecond = 0; 9 10 { 11 // 调用确定参数类型的方法的耗时 12 Stopwatch watch = new Stopwatch(); 13 watch.Start(); 14 for (int i = 0; i < 100000000; i++) 15 { 16 ShowInt(testVal); 17 } 18 watch.Stop(); 19 commonSecond = watch.ElapsedMilliseconds; 20 } 21 { 22 // 调用Object参数类型的方法的耗时 23 Stopwatch watch = new Stopwatch(); 24 watch.Start(); 25 for (int i = 0; i < 100000000; i++) 26 { 27 ShowObject(testVal); 28 } 29 watch.Stop(); 30 objectSecond = watch.ElapsedMilliseconds; 31 } 32 { 33 //调用泛型参数类型的方法的耗时 34 Stopwatch watch = new Stopwatch(); 35 watch.Start(); 36 for (int i = 0; i < 100000000; i++) 37 { 38 Show<int>(testVal); 39 } 40 watch.Stop(); 41 genericSecond = watch.ElapsedMilliseconds; 42 } 43 Console.WriteLine("commonSecond = {0} \n objectSecond = {1} \n genericSecond = {2} ", 44 commonSecond, objectSecond, genericSecond); 45 46 } 47 48 /// <summary> 49 /// 确定参数类型的方法 50 /// </summary> 51 /// <param name="valParam"></param> 52 private static void ShowInt(int valParam) 53 { 54 55 } 56 /// <summary> 57 /// Object参数类型的方法(类型不确定通过用obj 来实现通用) 58 /// </summary> 59 private static void ShowObject(int objParam) 60 { 61 62 } 63 /// <summary> 64 /// 泛型参数类型的方法 65 /// </summary> 66 private static void Show<T>(int tParam) 67 { 68 69 } 70 71 }
上面代码的执行效果:
通过上图可以得出:确定参数类型的方法和 泛型参数类型的方法相差不是很大,Object参数类型的方法的方法性能最差,Object慢的原因是多了一个装箱操作。
协变逆变
在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。
C# 4.0通过两个关键字:out和in来分别支持以协变和逆变的方式使用泛型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | class Program { static void Main( string [] args) { List<Bird> birdList1 = new List<Bird>(); // vs提示错误 //List<Bird> birdList2 = new List<Sparrow>(); List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList(); //协变 IEnumerable<Bird>既可以接受父类也可以接受子类 IEnumerable<Bird> birdList4 = new List<Bird>(); IEnumerable<Bird> birdList5 = new List<Sparrow>(); //逆变 ICustomerListIn<Sparrow>既可以接受父类也可以接受子类 ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>(); ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Sparrow>(); } } class Bird { } class Sparrow : Bird { } /// <summary> /// out 协变 只能是返回结果 /// </summary> /// <typeparam name="T"></typeparam> public interface ICustomerListOut< out T> { T Get(); } public class CustomerListOut<T> : ICustomerListOut<T> { public T Get() { return default (T); } } /// <summary> /// in 逆变 只能是参数 /// </summary> /// <typeparam name="T"></typeparam> public interface ICustomerListIn< in T> { void Show(T t); } public class CustomerListIn<T> : ICustomerListIn<T> { public void Show(T t) { } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?