第九讲 高级泛型类、泛型方法、泛型缓存
dynamic:这个关键字,是和泛型配合使用的,指示代码跳过编译期的数据检查,在运行期的时候在确定数据类型
使用泛型-->代码简洁、数据安全
public void GenericAdd<T1, T2>(T1 a, T2 b) where T1 : struct where T2 : struct
{
dynamic r1 = a;
dynamic r2 = b;
Console.WriteLine(r1 + r2);
}
//泛型使用的阶段:从程序开始到IL都是支持的
//.NET程序编译和运行过程:【程序编写-->编译器编译-->IL中间语言(exe/dll)】-->CLR中JIT编译-->机器语言(1001001)
//使用泛型还有另一个重要的作用:性能上还是非常高的
程序运行效率:明确类型最高,其次是泛型,最后是object类型
default:泛型初始化,如果是引用类型就是null,如果是其他类型就是0,或者空
//【3】泛型约束
public class GenericClass2<T1, T2, T3, T4, T5>
where T1 : struct //结构约束:类型参数必须是除了Nullable以外的任意的(int、double)
where T2 : class //引用约束:类型参数必须是类(普通类、接口、委托、数组...
where T3 : Book //基类约束:类型参数必须是Book或者其派生类
where T4 : IMyInterface //接口约束
where T5 : new() //无参数构造约束:类型参数必须是无参数构造方法,与其他约束一起使用的时候,要求必须放到最后
{
private T1 t1Value;
public List<T3> t3List { get; set; }
/// <summary>
/// 引用约束
/// </summary>
/// <param name="t2Value"></param>
public void ShowT2Object(T2 t2Value)
{
Console.WriteLine(t2Value.GetType());
}
/// <summary>
/// 结构约束
/// </summary>
/// <param name="t"></param>
public GenericClass2(T1 t)
{
t1Value = t;
t3List = new List<T3>();
}
/// <summary>
/// 基类约束
/// </summary>
/// <param name="t3Value"></param>
public void AddT3Object(T3 t3Value)
{
t3List.Add(t3Value);
}
/// <summary>
/// 接口约束
/// </summary>
/// <param name="t4Value"></param>
public void ShowT4Object(T4 t4Value)
{
t4Value.Do(3);
}
/// <summary>
/// 无参构造方法约束
/// </summary>
/// <param name="t5Value"></param>
public void ShowT5Object(T5 t5Value)
{
T5 t5 = new T5();//因为有约束所以可以大胆使用
List<T5> t5List = new List<T5>();
t5List.Add(t5);
t5List.Add(t5Value);
Console.WriteLine(t5List.Count);
}
}
调用方式为:
GenericClass2<int, Student, CSharp, IMyInterface, Book> gClass3 =
new GenericClass2<int, Student, CSharp, IMyInterface, Book>(10);
gClass3.AddT3Object(new CSharp());
gClass3.ShowT5Object(new SQLServer());
namespace ConsoleApplication13
{
/// <summary>
/// 泛型基础泛型方法
/// </summary>
public class JiChu
{
public void Add(int a,int b)
{
Console.WriteLine("一般方法"+(a+b));
}
public void Add(double a, double b)
{
Console.WriteLine("一般方法" + (a + b));
}
public void TAdd<T1,T2>(T1 a,T2 b) where T1 :struct where T2:struct
{
//这里我们使用dynamic ,可以让我们的代码,r1=a这样的代码,跳过编译期的校验在运行期在做处理,所以可以使用 r1+r2,
//如果没有dynamic 那a+b就会报错,因为我们不知道a和b的类型是什么,能不能使用+
//var u = a + b; 这个是报错的
dynamic r1 = a;
dynamic r2 = b;
Console.WriteLine("泛型方法" + (r1 + r2));
}
}
}
namespace ConsoleApplication13a
{
public class NiBian
{
//第一种替换原则,这个没有问题,子类替换父类
Car myCar = new Bus_Car();
//这个也是没有问题的,
List<Car> myListCar = new List<Car>();
//但是我们如果对list使用替换原则会怎么样,会报错,提示无法隐私吧List<Bus_Car>转化为List<Car>
// List<Car> myListCar2 = new List<Bus_Car>();
//那我们现在要是强制转化会怎么样,还是报错,提示无法吧List<Bus_Car>转化为List<Car>
// List<Car> myListCar3 = (List<Car>)new List<Bus_Car>();
//那如果我们想要这样转化要怎么做那,利用lambda,这样就可以了
List<Car> myListCar4 = new List<Bus_Car>().Select(c => (Car)c).ToList();
//如果对上面的不是太明白,那我们用你们方法看一下,lambda本身就是匿名方法,所以我们使用匿名方法也可以实现上面的逻辑
List<Car> myListCar5 = new List<Bus_Car>().Select(myt).ToList();
static Func<Bus_Car,Car> myt = delegate(Bus_Car t)
{
return (Car)t;
};
//其实我们还可以使用一个接口public interface IEnumerable<out T>
//我们发现这样也可以,为什么,这个就是IEnumerable的一个特殊性 out T说明这个接口支持协变
//协变:就是某个返回值类型,可以用他的子类类型替换,(对照替换原则)
IEnumerable<Car> myListCar6 = new List<Bus_Car>();
//这种事没有问题的一般使用
MyInf<Bus_Car> myListCar7 = new Test_MyInf<Bus_Car>();
//下面我们换一种尝试,吧new 后面的输入类型换成父类,(利用父类替换子类(这个很明显不符合替换原则,但是这个是对呀的输入参数))
//但是这个也可以,不会报错,为什么那,这种就叫做"逆变" 我们可以看我们的定义的接口 "in T"
//逆变:一个输入类型可以用它的父类替换(注意这里的替换是输入类型,注意和面向对象的替换原则区分)
MyInf<Bus_Car> myListCar8 = new Test_MyInf<Car>();
//下面这个是逆变和协变的综合体
static Func<Bus_Car, Bus_Car> myListCar9 = c =>
{
return new TT_Bus();
};
Func<TT_Bus, Car> myfun = myListCar9;
//我们可以看到,myListCar9中的第一个Bus_Car替换了myfun中的TT_Bus,父类替换子类,这个是输入参数,属于逆变
//而myListCar9.Bus_Car替换了myfun.Car,这个是输出参数,子类替换了父类,这个是协变
}
public class Car
{
public string carName { get; set; }
}
public class Bus_Car:Car
{
public string busName { get; set; }
}
public class TT_Bus:Bus_Car
{
public string ttName { get; set; }
}
public interface MyInf<in T>
{
void Show(T t);
}
public class Test_MyInf<T>:MyInf<T>
{
public void Show(T t)
{
Console.WriteLine("抽象接口");
}
}
}
namespace ConsoleApplication13
{
public class TestTCache
{
/// <summary>
/// 测试
/// </summary>
public static void Test()
{
Console.WriteLine("------------------------测试泛型缓存-------------------------");
//第一次初始化(会初始化静态成员和静态构造方法)下面会出现两次初始化
TCache<TData1>.AddT(new TData1() { dataName = "zzl" });
TCache<TData2>.AddT(new TData2() { dataName = "aaa" });
//正常情况下,我们创建的静态成员,静态构造函数,只会在拉第一次创建的时候执行,后面不会再执行
//但是上面TCache明显构造函数执行了两次,为什么那,这个就要说到泛型初始化的本质了,
//泛型的本质就是根据不同的数据类型,编译成不同的类,因为TCache<TData1>和TCache<TData2>的泛型数据类型不同,所以被编译成了不同的两个类,
//所以静态构造函数执行了两次
Console.WriteLine("TData1泛型数据" + TCache<TData1>.GetT().dataName);
Console.WriteLine("TData2泛型数据" + TCache<TData2>.GetT().dataName);
Console.WriteLine("------------------------重新泛型缓存赋值-------------------------");
//第二次初始化(会初始化静态成员和静态构造方法)下面会出现两次初始化
TCache<TData1>.AddT(new TData1() { dataName = "111" });
TCache<TData2>.AddT(new TData2() { dataName = "222" });
//这里是第二次初始化,因为泛型的本质就是,根据不同的泛型类型编译成不同的类,TCache<TData1>,TCache<TData2>这两个类型上面已经执行过一次了,所以不会再执行静态构造函数了
Console.WriteLine("TData1泛型数据第二次赋值" + TCache<TData1>.GetT().dataName);
Console.WriteLine("TData2泛型数据第二次赋值" + TCache<TData2>.GetT().dataName);
//结论:泛型的本质是根据不同的数据类型,编译成不同的类。TCache<TData2>中TCache 加上泛型类型TData2编译成了一个新类
}
}
/// <summary>
/// 泛型缓存
/// </summary>
public class TCache<T> where T:class,new()
{
private static T myTCachnu=null ;
static TCache()
{
Console.WriteLine("构造函数执行了");
}
/// <summary>
/// 添加泛型缓存
/// </summary>
/// <param name="t"></param>
public static void AddT(T t)
{
myTCachnu = t;
}
/// <summary>
/// 获取缓存
/// </summary>
/// <returns></returns>
public static T GetT()
{
return myTCachnu;
}
}
/// <summary>
/// 测试泛型类1
/// </summary>
public class TData1
{
public string dataName { get; set; }
}
/// <summary>
/// 测试泛型类2
/// </summary>
public class TData2
{
public string dataName { get; set; }
}
}
基于泛型实现缓存:泛型类 + 静态字段 + 静态方法