C#中泛型的使用
一、简介
简单记录一下C#中泛型类,泛型方法,泛型委托等泛型的使用,从.NetFramework2.0开始,支持泛型,通过反编译可以看到使用泛型的地方,都是使用了占位符 `1,当运行的时候,会将占位符替换成对应的类型
二、泛型方法
泛型方法就是为了解决不同类型要使用同样方法的问题,泛型方法需要在方法名的后面带一个<T>,T是类型参数,只是一个占位符,也可以用别的名字代替
2.1 泛型方法的声明
//实现string 转换为其他类型 public static T TypeConvert<T>(string val) { if (String.IsNullOrWhiteSpace(val)) return default(T); if (typeof(T).IsEnum) return (T)Enum.Parse(typeof(T), val, true); return (T)Convert.ChangeType(val, typeof(T)); }
2.2 泛型方法的调用
int num = TypeConvert<int>("1");//将字符l类型的1转换成int 类型的1
三、泛型类和泛型接口
3.1 泛型类的声明
泛型类就是一个类可以实现多个类型的需求
我们常用的集合List<T> 就是一个泛型类,可以转到定义看到其声明 public class List<T>
当然我们进行实例化的时候,必须要指定类型,List<string> names = new List<string>();
3.2 泛型接口
泛型接口,就是一个接口 满足多个多个类型的需求
//声明泛型接口 public interface GenericInterface<T> { }
四、泛型委托
泛型委托与普通的委托相比,它可以适应更多的类型
微软官方提供了两种泛型委托,一个是Action,无返回值,另一个是Func,带返回值
我们可以自定义泛型委托,如下所示
public delegate void GenericDelegate <T>(T para);
五、泛型缓存
泛型缓存: 会根据传入的不同类型,分别生成不同的副本 。主要针对不同类型,每次都会调用都会产生与之对应的相同的数据的现象,可以使用泛型缓存,将第一次生成的数据存放在缓存中,再次调用时直接返回该数据。
5.1 泛型缓存声明
public class GenericCache<T> { static GenericCache() { _Sql = GetSql(T); } private static string _Sql = ""; public static string GetCache() { return _Sql; } }
5.2 泛型缓存调用
//对于int 和 string 这两种类型,会生成不同的副本,生成的sql不一样,当第二次调用时,则不需要再重新生成sql,可直接返回第一次生成的sql,对于不同的类型,缓存的数据也都不同 GenericCache<int>.GetCache(); GenericCache<string>.GetCache();
六、泛型约束
泛型约束,顾名思义,就是传入的类型进行约束,防止任何类型都可以传进去导致出现异常
public static void GenericMethod<T>(T t) where T : class // 引用类型约束 就只能传入引用类型的参数 { } public static void GenericMethod1<T>(T t) where T : struct // 结构体类型 int float ... { } public static void GenericMethod2<T>(T t) where T : new() // 无参数公共构造函数约束 { } public static void GenericMethod3<T>(T t) where T : P //基类约束,传入类型必须是P或P的派生类 { } public static void GenericMethod4<T>(T t) where T : IP //接口约束,传入类型必须实现接口IP { }
七、协变与逆变
7.1 协变跟逆变是对泛型类的继承关系的表述
在使用泛型的时候,会存在一些不和谐的地方,首先准备两个继承关系的类看一下
public class Animal { public int TypeName { get; set; } } public class Cat: Animal { public int Id { get; set; } }
Cat继承于Animal,当使用集合List时,就会发现存在矛盾
Animal cat = new Cat(); List<Cat> cats = new List<Cat>(); List<Animal> animals = new List<Animal>(); List<Animal> animal_cats = new List<Cat>();//编译器报错
按理说,一只猫是一个动物,一群猫也应该是一群动物才对,这里创建时就会发现编译器会报错,原因就是List<Animal> 与 List<Cat>是两个类,不存在继承关系,在这里就需要使用到协变和逆变了
7.2 协变
使用IEnumerable实现
IEnumerable<Animal> cats = new List<Cat>();
也可自己定义模仿IEnumerable 定义接口实现,需要使用到out
public interface IMyListOut<out T> { T Show(); } public class MyListOut<T> : IMyListOut<T> { public T Show() { return default; }
}
IMyListOut<Animal> myListOut = new MyListOut<Cat>();//使用
在使用协变时,需要使用out 修饰,并且左边只能传入基类,只能作为返回值而不能作为参数
7.3 逆变
逆变需要使用到关键字in
public interface IMyListIn<in T> { void Show(T t); } public class MyListIn<T> : IMyListIn<T> { public void Show(T t) { Console.WriteLine(t.GetType()); } }
IMyListIn<Cat> myListIn = new MyListIn<Animal>();
in用于修饰传入的子类,且只能为参数,不可为返回值
7.4 协变与逆变同时使用
public interface IMyListOutAndIn<in Tin, out Tout> { void ShowT(Tin t); Tout GetT(); Tout Together(Tin t); } public class MyListOutAndIn<Tin, Tout> : IMyListOutAndIn<Tin, Tout> { public Tout GetT() { return default; } public void ShowT(Tin t) { Console.WriteLine(t.GetType()); } public Tout Together(Tin t) { Console.WriteLine(t.GetType()); return default; } }
IMyListOutAndIn<Cat, Animal> myListOutAndIn = new MyListOutAndIn<Animal, Cat>();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)