业务应该这么写--泛型

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

       泛型这个东西,只要讨厌重复代码,追求高质量代码的程序员都肯定有用过。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);
  }

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

      

猜你喜欢

转载自www.cnblogs.com/gdouzz/p/8960759.html
今日推荐