解读经典《C#高级编程》泛型 页122-127.章4
前言
本篇继续讲解泛型。上一篇讲解了泛型类的创建。本篇讲解泛型类创建和使用的细节。
泛型类
上篇举了个我产品中用到的例子,本篇的功能可以对照着此案例进行理解。
/// <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的更多相关文章
- C#高级编程笔记之第三章:对象和类型
类和结构的区别 类成员 匿名类型 结构 弱引用 部分类 Object类,其他类都从该类派生而来 扩展方法 3.2 类和结构 类与结构的区别是它们在内存中的存储方式.访问方式(类似存储在堆上的引用类型, ...
- 《Node.js 高级编程》简介与第二章笔记
<Node.js 高级编程> 作者简介 Pedro Teixerra 高产,开源项目程序员 Node 社区活跃成员,Node公司的创始人之一. 10岁开始编程,Visual Basic.C ...
- C#高级编程学习一-----------------第五章泛型
三层架构之泛型应用 概述 1.命名约定 泛型类型以T开头或就是T. 2.泛型类 2.1.创建泛型类
- C#高级编程第9版 第一章 .NET体系结构 读后笔记
.NET的CLR把源代码编译为IL,然后又把IL编译为平台专用代码. IL总是即时编译的,这一点的理解上虽然明白.当用户操作C#开发的软件时,应该是操作已经编译好的程序.那么此时安装在客户机上的程序是 ...
- 20191105 《Spring5高级编程》笔记-第5章
第5章 Spring AOP 面向切面编程(AOP)是面向对象编程(OOP)的补充.AOP通常被称为实施横切关注点的工具.术语横切关注点是指应用程序中无法从应用程序的其余部分分解并且可能导致代码重复和 ...
- 20191105 《Spring5高级编程》笔记-第12章
第12章 使用Spring远程处理 12.4 在Spring中使用JMS 使用面向消息的中间件(通常成为MQ服务器)是另一种支持应用程序间通信的流行方法.消息队列(MQ)服务器的主要优点在于为应用程序 ...
- 20191105 《Spring5高级编程》笔记-第9章
第9章 事务管理 一些名词: 2PC(2 Phase Commit) XA协议 JTS(Java Transaction Service) JCA(Java EE Connector Architec ...
- 20191105 《Spring5高级编程》笔记-第6章
第6章 Spring JDBC支持 Spring官方: 位于Spring Framework Project下. 文档: https://docs.spring.io/spring-framework ...
- 20191103 《Spring5高级编程》笔记-第4章
第4章 详述Spring配置和Spring Boot 4.2 管理bean生命周期 通常,有两个生命周期事件与bean特别相关:post-initialization和pre-destruction. ...
- C#高级编程笔记(11至16章)异步/托管/反射/异常
11.1.2LINQ语句 LINQ查询表达式以from子句开始,以select或者group子句结束.在这两个子句之间可以跟零个或者多个from.let.where.join或者orderby子句. ...
随机推荐
- JUC
1.Java JUC简介 在Java5.0提供了java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池.异 ...
- H5上传功能
近期开发一个关于微信公总号二次开发中,上传图片的需求,测试几个开源插件,更新一些心得,有需要可留言!!! plupload plupload多张上传图片的一个参考demo ajaxFileUpload ...
- 旧项目Makefile 迁移CMake的一种方法:include Makefile
有些c++旧项目用Makefile,要迁移CMake的时候非常痛苦,有些像static pattern的语法和make自带命令 cmake要重写一套非常的麻烦. 因此这里用trick的方法实现了一种i ...
- 用kattle将数据从SQLserver中导入到vertica中
今天简单的学习了一下ETL工具kattle了,只是简单的上手,不过这也已经够我去做POC了. 首先大体介绍一下kattle,Kettle是一款国外开源的ETL工具,纯java编写,可以在Window. ...
- 判断js中的数据类型的几种方法
判断js中的数据类型有一下几种方法:typeof.instanceof. constructor. prototype. $.type()/jquery.type(),接下来主要比较一下这几种方法的异 ...
- 小程序----选择地理位置 ( wx.chooseLocation ) 和 获取地理位置 (wx.getSetting)
问题来了:假如我第一次使用wx.chooseLocation()获取权限被拒绝,然后使用wx.getSetting()来重新获取权限该怎么做呢? 思路:wx.chooseLocation()有fail ...
- Android Studio 错误: 非法字符: '\ufeff'
右下角:选UTF-8 convert一下,再重新编译..不知道为什么,本来好像就是UTF-8好奇怪. 还看到一个方法但我没试过,放在这里万一下次又遇见了这样的问题呢 右下角将UTF-8 convert ...
- Ubuntu环境下配置darknet
本教程基于Linux物理机进行相关配置,要求物理机中包含N卡且Capbility>=3.0,小于3.0(Fermi架构)只允许配置cuda,不能配置使用Cudnn: 本教程分为: 1.安装NVI ...
- NodeJS NPM 镜像使用方法
每次npm的时候,走国外的镜像,非常的慢,可以配置一下 通过改变默认npm镜像代理服务,以下三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候不用重新配置. 通过config命令 ...
- Redis两种方式实现限流
案例-实现访问频率限制: 实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次. 非脚本实现 private boolean accessLimit(String ip, int ...