一、简介
简单记录一下C#中泛型类,泛型方法,泛型委托等泛型的使用,从.NetFramework2.0开始,支持泛型,通过反编译可以看到使用泛型的地方,都是使用了占位符 `1,当运行的时候,会将占位符替换成对应的类型
二、泛型方法
泛型方法就是为了解决不同类型要使用同样方法的问题,泛型方法需要在方法名的后面带一个,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 就是一个泛型类,可以转到定义看到其声明 public class List
当然我们进行实例化的时候,必须要指定类型,List names = new List();
3.2 泛型接口
泛型接口,就是一个接口 满足多个多个类型的需求
//声明泛型接口
public interface GenericInterface<T>
{
}
四、泛型委托
泛型委托与普通的委托相比,它可以适应更多的类型
微软官方提供了两种泛型委托,一个是Action,无返回值,另一个是Func,带返回值
我们可以自定义泛型委托,如下所示
public delegate void GenericDelegate (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 与 List是两个类,不存在继承关系,在这里就需要使用到协变和逆变了
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>();