代码改变世界

解读经典《C#高级编程》泛型 页122-127.章4

2019-03-05 17:46  圣殿骑士18  阅读(471)  评论(0编辑  收藏  举报

前言


本篇继续讲解泛型。上一篇讲解了泛型类的创建。本篇讲解泛型类创建和使用的细节。

泛型类

上篇举了个我产品中用到的例子,本篇的功能可以对照着此案例进行理解。

/// <summary>
/// 单一事务处理服务,用于单表的数据读写事务
/// </summary>
/// <typeparam name="TViewModel"></typeparam>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TDbContext"></typeparam>
public class EFRepository<TViewModel, TEntity, TDbContext> : IDisposable
    where TEntity : class,new()
    where TViewModel : class,new()
    where TDbContext : DbContext,new()
{
    private DbContext dbContext;
    private DbSet<TEntity> dbSet;

    /// <summary>
    /// 构造方法
    /// </summary>
    public EFRepository()
    {
        dbContext = new TDbContext();
        dbSet = dbContext.Set<TEntity>();
    }

    /// <summary>
    /// 根据主键获取单条数据
    /// </summary>
    /// <param name="keyValues"></param>
    /// <returns></returns>
    public TViewModel Get(params object[] keyValues)
    {
        return dbSet.Find(keyValues)
            .MapTo<TEntity, TViewModel>();
    }

    /// <summary>
    /// 新增单条数据
    /// </summary>
    /// <param name="model"></param>
    public void Add(TViewModel model)
    {
        var entity = model.MapTo<TViewModel, TEntity>();
        dbSet.Add(entity);
        dbContext.SaveChanges();
    }

    /// <summary>
    /// 根据主键删除单条数据
    /// </summary>
    /// <param name="keyValues"></param>
    public void Delete(params object[] keyValues)
    {
        TEntity entity = dbSet.Find(keyValues);
        dbSet.Remove(entity);
        dbContext.SaveChanges();
    }
}

默认值

T作为泛型类型,有时候会需要取默认值。我们知道,引用类型的默认值是null,数字类型的默认值是0,但泛型类型T既可能是引用类型,也可以是值类型,那默认值就是不定的。怎么解决这个问题?系统提供了default关键字。

var val1 = default(int);    //指定类型默认值
var val2 = dafault(T);      //泛型类型默认值,dafault(T)在泛型类内部实现上经常用到

约束

在前面的案例中,where语句中的定义就是约束。

where TEntity : class,new()
where TViewModel : class,new()
where TDbContext : DbContext,new()

约束的目的是,限定泛型类型的类型,从而使得泛型类型内部的实现代码可以安全的使用约束条件下的功能方法。
约束类型可以有多种:

where T: class      //T必须是类(引用类型)
where T: struct     //T必须是结构
where T: Foo        //T必须来自基类Foo
where T: IFoo       //T必须继承接口IFoo
where T2: T1         //T2必须继承自泛型类型T1
where T: new()      //T必须定义一个默认构造函数(构造函数约束只能定义在默认构造函数上,不支持其他重载的构造函数)
where T: Foo, new()     //使用逗号,多个约束可叠加

构造函数约束
首先定义构造函数约束的目的,是因为在泛型类内部需要对泛型类型进行初始化,因此要指定约束以保证类型初始化能顺利完成。
而构造函数约束只支持默认构造函数,应该是因为:如果泛型类型定义了多参数构造函数,其构造过程是个难题,初始化的参数如何传入泛型类呢?再引入新的泛型类型?那这个逻辑就死循环了,而且泛型类最终是要通过JIT编译器生成新类的代码的,这会让JIT编译器的实现难度增大。

继承

泛型类的继承还是比较灵活的,可以有以下多种继承方式:

public class MyList1<T> : List<T>
{
    //定义泛型类继承自泛型类:泛型类型相同
}

public class MyList2<T> : List<string>
{
    //定义泛型类继承自泛型类:基类确定了泛型类型
}

public class MyList3<T> : IEnumerable<T>
{
    //定义泛型类继承自泛型接口:泛型类型相同
}

public class MyList4 : List<string>
{
    //定义非泛型普通类:基类是明确了泛型类型的泛型类
}

静态成员

泛型类的j静态成员只能在类的一个实例中共享。

public class StaticG<T>
{
    public static int index;
}

Main()方法测试:
StaticG<string>.index = 1;
StaticG<int>.index = 2;

Console.WriteLine(StaticG<string>.index);   //输出1
Console.WriteLine(StaticG<int>.index);      //输出2

回顾前面讲到的原理,这个也是好理解的。因为虽然静态成员定义只在一个类中,但JIT编译器根据不同的T类型编译成的是不同的临时新类。这个例子中,T为int和T为string,会分别被编译成两个新类,那么静态成员分别属于各自的类,数据当然存储为各自不同的两份了。


本篇主要围绕泛型类的定义,讲解定义中的各种特性。下一篇,我们继续讲泛型的更多细节。


觉得文章有意义的话,请动动手指,分享给朋友一起来共同学习进步。
欢迎关注本人如下公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。

微信公众号
扫描二维码关注

回到目录,再看看相关文章