前言

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

泛型类

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

/// <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)、技术专题、视野知识、源码下载等内容。



扫描二维码关注

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

解读经典《C#高级编程》泛型 页122-127.章4的更多相关文章

  1. C#高级编程笔记之第三章:对象和类型

    类和结构的区别 类成员 匿名类型 结构 弱引用 部分类 Object类,其他类都从该类派生而来 扩展方法 3.2 类和结构 类与结构的区别是它们在内存中的存储方式.访问方式(类似存储在堆上的引用类型, ...

  2. 《Node.js 高级编程》简介与第二章笔记

    <Node.js 高级编程> 作者简介 Pedro Teixerra 高产,开源项目程序员 Node 社区活跃成员,Node公司的创始人之一. 10岁开始编程,Visual Basic.C ...

  3. C#高级编程学习一-----------------第五章泛型

    三层架构之泛型应用 概述 1.命名约定 泛型类型以T开头或就是T. 2.泛型类 2.1.创建泛型类

  4. C#高级编程第9版 第一章 .NET体系结构 读后笔记

    .NET的CLR把源代码编译为IL,然后又把IL编译为平台专用代码. IL总是即时编译的,这一点的理解上虽然明白.当用户操作C#开发的软件时,应该是操作已经编译好的程序.那么此时安装在客户机上的程序是 ...

  5. 20191105 《Spring5高级编程》笔记-第5章

    第5章 Spring AOP 面向切面编程(AOP)是面向对象编程(OOP)的补充.AOP通常被称为实施横切关注点的工具.术语横切关注点是指应用程序中无法从应用程序的其余部分分解并且可能导致代码重复和 ...

  6. 20191105 《Spring5高级编程》笔记-第12章

    第12章 使用Spring远程处理 12.4 在Spring中使用JMS 使用面向消息的中间件(通常成为MQ服务器)是另一种支持应用程序间通信的流行方法.消息队列(MQ)服务器的主要优点在于为应用程序 ...

  7. 20191105 《Spring5高级编程》笔记-第9章

    第9章 事务管理 一些名词: 2PC(2 Phase Commit) XA协议 JTS(Java Transaction Service) JCA(Java EE Connector Architec ...

  8. 20191105 《Spring5高级编程》笔记-第6章

    第6章 Spring JDBC支持 Spring官方: 位于Spring Framework Project下. 文档: https://docs.spring.io/spring-framework ...

  9. 20191103 《Spring5高级编程》笔记-第4章

    第4章 详述Spring配置和Spring Boot 4.2 管理bean生命周期 通常,有两个生命周期事件与bean特别相关:post-initialization和pre-destruction. ...

  10. C#高级编程笔记(11至16章)异步/托管/反射/异常

    11.1.2LINQ语句 LINQ查询表达式以from子句开始,以select或者group子句结束.在这两个子句之间可以跟零个或者多个from.let.where.join或者orderby子句. ...

随机推荐

  1. 我理解的websocket

    短轮询:客户端发起请求,服务器无论有无消息都返回信息,结束http连接.然后继续发起请求. 长轮询:客户端发起请求,建立连接,直到服务端返回消息response,结束http连接.然后继续发起请求,重 ...

  2. lnmp环境一些基本命令行

    使用service启动/停止/重启相关服务 启动/停止/重启 php服务 service php-fpm start/stop/restart 启动/停止/重启 mysql service mysql ...

  3. TS+React+Redux 使用之搭建环境

    使用 create-react-app 构建 1.全局安装create-react-app npm install -g create-react-app 2.创建一个项目 create-react- ...

  4. 关于外网无法访问阿里云主机CentOs

    前两天阿里云ECS搞活动,所有买了个三年的Ecs,然后照着之前在虚拟机同样的搭建服务器,一切都很正常,可是 当我配置好防火墙和nginx之后,发现个问题,外网无法访问. 思考: 1.我的nginx没配 ...

  5. [LeetCode] Keys and Rooms 钥匙与房间

    There are N rooms and you start in room 0.  Each room has a distinct number in 0, 1, 2, ..., N-1, an ...

  6. 了解vue APi

    阳光那么好,何必自寻烦恼,过好每一个当下,一万个美丽的未来抵不过一个温暖的现在. 一.Vue.nextTick(): 该api 是在Dom节点更新结束之后执行的一个延时回调.在修改数据之后,立即使用这 ...

  7. 搭积木(java)-蓝桥杯

    搭积木小明最近喜欢搭数字积木,一共有10块积木,每个积木上有一个数字,0~9.搭积木规则:每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小.最后搭成4层的金字塔形,必须用完所有的积木.下 ...

  8. 构建一个 预装 pm2 的 node 项目 docker 底包

    Dockerfile: 创建 dockerfile 文件, 命名为 dockerfile-yourProject-node.8.12.0-pm2 # MAGE: yourGroup/yourProje ...

  9. 大量示例彻底搞懂Linux查找,which,whereis,locate,find

    前言 Linux常用命令中,有些命令可以帮助我们查找二进制文件,帮助手册或源文件的位置,也有的命令可以帮助我们查找磁盘上的任意文件,今天我们就来看看这些命令如何使用. which which命令会在P ...

  10. [Swift]LeetCode728. 自除数 | Self Dividing Numbers

    A self-dividing number is a number that is divisible by every digit it contains. For example, 128 is ...