第九讲 高级泛型类、泛型方法、泛型缓存

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; }
    }
}
 
 
基于泛型实现缓存:泛型类  +  静态字段  + 静态方法
 
 
 
 
 
 
 
posted @ 2019-04-08 18:11  瀚海行舟  阅读(750)  评论(1编辑  收藏  举报