学习笔记:C#高级进阶语法——泛型(Generic)
1.学习笔记:C#高级进阶语法——反射(Reflection)
2.学习笔记:C#高级进阶语法——泛型(Generic)
3.学习笔记:C#高级进阶语法——特性(Attribute)4.学习笔记:C#高级进阶语法——委托(Delegate)一、泛型(Generic)
1、泛型的定义:通用的类型就是泛型
//在一个方法,传入的参数不确定的时候,我们可能要重写多次这个方法 public void Show(string t) { Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}"); } public void Show(int t) { Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}"); }
//这种情况也可以将参数定义为object类型来解决这个问题 public void Show(object t) { Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}"); }
但是object有两个缺陷: 1、类型安全问题 2、性能非常差 通过下面的类子来证明这个问题
using System.Diagnostics; namespace MyGeneric { public class Monitor { public static void Show() { int iValue = 12345; long commonSecond = 0; long objectSecond = 0; long genericSecond = 0; { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100_000_000; i++) { ShowInt(iValue); } sw.Stop(); commonSecond = sw.ElapsedMilliseconds; } { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100_000_000; i++) { ShowObject(iValue); } sw.Stop(); objectSecond = sw.ElapsedMilliseconds; } { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100_000_000; i++) { ShowGeneric(iValue); } sw.Stop(); genericSecond = sw.ElapsedMilliseconds; } Console.WriteLine($"commonSecond={commonSecond},objectSecond={objectSecond},genericSecond={genericSecond}"); } public static void ShowInt(int iValue) { } public static void ShowObject(object oValue){} public static void ShowGeneric<T>(T t) { } } }
//所以我们需要泛型来解决这个问题,泛型的性能和固定类型的性能没有什么差距 public void Show<T>(T t) { Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}"); }
2、泛型的声明---设计思想
方法在声明的时候,带有尖括号<>,尖括号里有一个占位符T;
T是什么?什么类型? 称之为类型参数
延迟声明:声明时不确定类型,调用时确定类型
3、泛型的特点和原理---在底层如何支持
1、泛型在编译后,变成一个带有 `符号[位数]
2、泛型类在继承时,子类继承的父类
4、泛型的多种应用
1、泛型方法----方法名<T> 2、泛型类---类名<T>,特点:为每一个不同类型生成一个唯一的副本 3、泛型接口--接口名<T> 4、泛型委托--Action<int>,Func<int>系统自带的两个泛型委托 5、泛型约束--
5、泛型缓存
using System; using System.Collections.Generic; using System.Threading; namespace MyGeneric.Extension { //能理解80%以上 刷个1 //能理解60%以上 刷个2 /// <summary> /// 泛型缓存~ /// </summary> public class GenericCacheTest { public static void Show() { //普通缓存 { for (int i = 0; i < 5; i++) { Console.WriteLine(DictionaryCache.GetCache<int>()); //GenericCacheInt Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache<long>());// GenericCachelong Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache<DateTime>()); Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache<string>()); Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache<GenericCacheTest>()); Thread.Sleep(10); } } //泛型缓存--可以根据不同的类型生成一个新的类的副本;生合二老无数的副本; { for (int i = 0; i < 5; i++) { Console.WriteLine(GenericCache<int>.GetCache()); //GenericCacheInt Thread.Sleep(10); Console.WriteLine(GenericCache<long>.GetCache());// GenericCachelong Thread.Sleep(10); Console.WriteLine(GenericCache<DateTime>.GetCache()); Thread.Sleep(10); Console.WriteLine(GenericCache<string>.GetCache()); Thread.Sleep(10); Console.WriteLine(GenericCache<GenericCacheTest>.GetCache()); Thread.Sleep(10); } } } } /// <summary> /// 字典缓存:静态属性常驻内存 /// </summary> public class DictionaryCache { /// <summary> /// 静态字典---常驻内存~ 只要程序进程不停止,就会一直保存数据在内存; /// </summary> private static Dictionary<Type, string> _TypeTimeDictionary = null; //同一个类的静态构造函数在整个进程中,执行且只执行一次; static DictionaryCache() { Console.WriteLine("This is DictionaryCache 静态构造函数"); _TypeTimeDictionary = new Dictionary<Type, string>(); } public static string GetCache<T>() { Type type = typeof(T); //获取类的type 类型 if (!_TypeTimeDictionary.ContainsKey(type)) { //写死的,生成数据; 使用当前这个type 作为key 保存到字典中去 _TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}"; } return _TypeTimeDictionary[type]; } } /// <summary> ///泛型缓存: ///本质就是泛型类---泛型缓存的应用--手写ORM----有应用~~ /// </summary> /// <typeparam name="T"></typeparam> public class GenericCache<T> { /// <summary> ///同一个类的静态构造函数在整个进程中,执行且只执行一次; /// </summary> static GenericCache() { Console.WriteLine("This is GenericCache 静态构造函数"); //_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); _TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}"; } /// <summary> /// 只缓存一个字符串 /// </summary> private static string _TypeTime = ""; public static string GetCache() { return _TypeTime; } } }
6、泛型约束
1、C#中引用类型-->保存在进程堆,值类型-->保存在线程栈,从值类型转换为引用类型叫装箱,从引用类型转换为值类型叫拆箱
2、类型安全:允许传入,但是在方法内部又写了类型强转等,在执行的时候会报错。
public static void ShowObject(object oParameter) { //Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={oParameter.GetType().Name},type={oParameter}"); //以上业务其实太简单了点; //如果想要用People呢? //C#是强类型语言,编译时决定类型; //编译器识别的 oParameter 当成 object来识别的;而object 是没有Id和Name //访问不了 //Console.WriteLine($"People.Id={oParameter.Id}"); //Console.WriteLine($"People.Name={oParameter.Name}"); //访问不了 //类型转换 People people = (People)oParameter; //类型强转~ Console.WriteLine($"People.Id={people.Id}"); Console.WriteLine($"People.Name={people.Name}"); //访问不了 } ////类型不安全: ////object 类型作为参数,所有类型都可以传入; ////在方法的内部业务处理,可能会因为一些不适合的类型传入,导致一些问题; ////特点: 允许你传入 //// 可能会报错~~ GenericConstraint.ShowObject(people); GenericConstraint.ShowObject(dtValue); ////最好的情况: 如果方法内部会出错,就应该不让你传入; 如果能传入,就要保证一定没问题;
3、泛型约束:
//特点: //有了约束---传入参数有了局部限制; //可以享有一些额外的权利~~ //有约束才有自由~~ 开车~~ 红绿灯; /// <summary> /// 基类约束: /// 泛型的类型参数,必须是People或者是People的子类 /// 否则无法传入; /// 就可以把这个类型参数当做People来使用; /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tParameter"></param> public static void ShowGeneric<T>(T tParameter) where T : People { //Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={tParameter.GetType().Name},type={tParameter}"); Console.WriteLine($"People.Id={tParameter.Id}"); Console.WriteLine($"People.Name={tParameter.Name}"); //访问不了 } ///接口约束 ///类型参数:必须实现这个接口,才能够传入进来; ///就可以把参数当成这个接口使用 public static void ShowGeneric<T>(T tParameter) where T : IWork { tParameter.Work(); } ///无参数构造函数约束 ///传入的参数,必须包含一个无参数构造函数 ///就可以直接new T 执行无参数构造函数~ public static void ShowGeneric<T>(T tParameter) where T : new() { T tt = new T(); } ///值类型约束 ///必须要传入结构类型,否则无法传入的 public static void ShowGeneric<T>(T tParameter) where T : struct { } ///引用类型约束 ///必须要传入引用类型,否则无法传入的 public static void ShowGeneric<T>(T tParameter) where T : class { } ///枚举约束 ///要求传入的参数必须是一个枚举 public static void ShowGeneric<T>(T tParameter) where T : Enum { } public static void ShowGenericNew<T,S,Y,Richard>(T tParameter) where T : struct { } public static void ShowGenericNew2<T, S>(T tParameter) where T : class where S : struct { }
7、协变、逆变
1、协变逆变:只针对泛型接口和泛型委托
//任何子类都可以用父类来声明 Animal animal = new Cat();//猫是一个动物 //但是在集合中这样声明会报错 List<Animal> animalList = new List<Cat>();//一堆猫是一堆动物,逻辑上是对的,但是声明的时候会报错 //因为泛型类在传入一个类的类型后,就生成了一个独立的副本,两边生成的副本都是相互独立的,所以左右两边的副本不存在继承关系 //以上的不和谐,引入的协变逆变 //协变,IEnumerable的泛型类型参数用out修饰,类型参数只能作为返回值,不能作为传入参数 //效果:可以让左边使用父类,右边使用子类 IEnumerable<Animal> animals = new List<Cat>(); //逆变, in修饰T,T只能做参数,不能做返回值 //效果:左边使用子类,右边使用父类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!