zoukankan      html  css  js  c++  java
  • 业务应该这么写--泛型

           技术太菜,做不了架构,只能写业务,但是做架构的心永远都在,所以我要开始玩转业务代码。

           泛型这个东西,只要讨厌重复代码,追求高质量代码的程序员都肯定有用过。C#自带的泛型使用案例很多,Linq,感觉无处不在的都是泛型阿。

        (一)泛型的基本概念

            泛型的出现是在.net framework2.0之后,基于泛型,我们可以将类型参数化,以便更大范围类地进行代码复用。同时,它减少了泛型及泛型方法中的转型,确保了类型安全。

            1、将类型参数化,代码复用更高;

            2、类型安全;

            3、高效率;

           下面分别对这三点,进行阐述;

           对于可重用性,比如要设计一个集合类;

     public class MyList
      {
        public static int Size = 100;
        public int[] Items = new int[Size];
    
        public int this[int i] {
          get { return Items[i]; }
          set { Items[i] = value; }
        }
      }

          该类型只支持整形,如果要支持字符串,有一种方法是重新添加一个类。但是这两个类型属性和方法都非常接近,如果有一种方法可以类型作为一个通用的数据类型,那么就可以进行代码复用了,同时类型也只要一个就够了。泛型完成的就是这样的功能。

      public class MyList<T> {
        public static int Size = 100;
        public T[] Items = new T[Size];
    
        public T this[int i] {
          get { return Items[i]; }
          set { Items[i] = value; }
        }
      }

         继续从刚刚那个问题出发,除了新增加一个类,另外一种方法是样MyList的编码从object的角度去设计(因为C#世界里面,所以类型(值类型和引用类型都是继承自object)。

      public class MyList {
        public static int Size = 100;
        public object[] Items = new object[Size];
    
        public object this[int i] {
          get { return Items[i]; }
          set { Items[i] = value; }
        }
      }

           使用的时候如下写法:

      MyList list = new MyList();
      list[0] = 123;
      list[1] = "123";

        这个就会带来类型不安全了,虽然编译能过,但是这里会导致一些隐藏的Bug,很难发现。同时也可以避免装箱拆箱的操作带来的性能损耗。

       (二)泛型的深入

            2.1泛型本质

          泛型的本质就是一个占位符;在C#泛型编译生成的IL代码中,T就是一个占位符的角色。在运行时,即时编译器(JIT)会用实际代码中输入的T类型来代替T,也就是说,在由JIT生成的本地代码中,已经使用了实际的数据类型。我们可以把MyList<int>和MyList<String>看成是两个完全不同的类型,但是这个仅仅是对本地代码而言的。对于实际的C#代码,它拥有的一个类型,那就是泛型类型MyList<T>。

           

          2.2 泛型的约束

          如果泛型不给约束,就是太过自由了,任何类型都可以进来。在现在这个社会,没有约束就没有自由,自由和约束是一起存在的,下面我们来看看泛型的约束。

          有了约束之后,泛型给我们的不仅仅是个对象,还可以是对象里面的方法和属性。

          1、基类约束

          这个可能是用得最多的了,用基类约束的好处是1、可以使用基类的一切属性方法;2、可以保证这个一定是基类或者基类的子类; 

      public class SalaryComputer {
        public int Compare<T>(T t1, T t2) where T : Salary {
          if (t1.BaseSalary > t2.BaseSalary) {
            return 1;
          }
          else if (t1.BaseSalary < t2.BaseSalary) {
            return -1;
          }
          return 0;
        }
      }
    
    
      public class Salary {
        public int BaseSalary { get; set; }
    
        public int Bonus { get; set; }
      }

          2、引用类型约束

        public void Method1<T>(T t) where T : class
        {
          
        }

          3、值类型约束

       public void Method2 <T>(T t) where T : struct {
          
        }

          4、无参数构造函数约束

     public void Method3<T>(T t) where T : new() {
    
        }

          5、接口约束

         2.3 default关键字

          使用default为泛型类型的变量指定初始值;引用类型的变量默认初始值就是NULL,而值类型的变量就不好说了,可能为零,也可能为其他。

      (三)协变,逆变

         协变和逆变都是用在泛型接口或者委托上面的,协变是out 关键字(只能作为返回值),逆变是in 关键字(只能作为参数);

         先记住上面,然后来看例子;

     public class Bird
      {
          
      }
    
      public class Sparrow:Bird
      {
        
      }

        先上面定义了两个类,一个是鸟,一个是继承自鸟的麻雀类。

       

    为了解决我们上面这个问题,一群麻雀应该是要属于一群鸟的情况,C#在.net framework4.0引入了out关键字。

       //这样子做,可以的话呢,是因为IEnumerable泛型里面是有out T 协变的,只能用在返回值上面。
          IEnumerable<Bird> birds4 = new List<Sparrow>();

    逆变呢就是刚刚好反过来。把一群父类复制给一群子类。逆变是用in;

     public interface IMyList<in T>{
        //逆变,只能用在返回值这边。
        void Get(T t);
      }
    
      public class MyList<T> : IMyList<T> {
        public void Get(T t) {
          throw new NotImplementedException();
        }
      }
          IMyList<Sparrow> sparrows=new MyList<Bird>();

         上面这个就是逆变和协变,但是说实话的,平常很少用到。

    (四)泛型在业务中的一些应用

            4.1 泛型方法,泛型类,泛型接口

             知道这些怎么用呢,刚刚开始是去掌握这些概念,去看别人写的代码,去学习观摩,然后去重构自己的烂代码...

            4.2 典型的应用场景

                    4.2.1泛型缓存类 

    public static class ListStaticCache<T> where T : class{
        /// <summary>
        /// 版本:可以从数据库的表,或者配置文件中读取,每次取值查询是否更新缓存
        /// </summary>
        private static string _version = string.Empty;
    
        /// <summary>
        /// 缓存内容
        /// </summary>
        private static List<T> _genericCache = null;
    
    
        //缓存的时候,还要加上版本号,错误信息等等东西
        /// <summary>  
        /// 数据库:该参数表所在的数据库名  
        /// </summary>  
        private static string _database = string.Empty;
        /// <summary>  
        /// 错误信息:保存缓存数据失败时的错误信息  
        /// </summary>  
        public static string ErrMsg { get; set; } = string.Empty;
    
        private static void ReadData()
        {
           //去数据库读取数据
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static List<T> Getcache(){
          //加了个版本号
          if (_genericCache == null || ConfigurationManager.AppSettings.Get(typeof(T).Name + ".version") != _version){
            
          }
          return _genericCache;
        }
    
      }

                   4.2.2 数据库通用操作类

            数据库里面通用的方法,根据不同实体类,通过反射来进行生成对应的SQL语句;

    public interface IDbHelper
      {
        void Insert<T>(T t);
        void Update<T>(T t);
        void Delete<T>(T t);
        void Query<T>(T t);
      }

            上面的具体代码,我迟一点会公布出来;请大家多多指教。

          

  • 相关阅读:
    初步学习next.js-1-新建项目
    对象比较-深层,浅层
    制作右键菜单
    使用高德API-初级应用
    启动前后端连载方法
    使用websocket
    关于图片压缩
    归并排序(mergesort)
    冒泡排序
    递归介绍
  • 原文地址:https://www.cnblogs.com/gdouzz/p/8960759.html
Copyright © 2011-2022 走看看