距离上次发表博客已经有几年了. 对于没能坚持更新博客,实在是感觉到甚是惭愧.

闲言少叙, 直接切入主题.

背景

最近一直在对于公司一个网络通信服务程序使用.net core 进行重构.重构的目的有两个:一是让程序能够跨平台运行. 二是优化程序代码结构是程序的可维护性有所提升.  重构的过程主要由我来设计底层的架构. 在这个过程中,由于我对C# 泛型的理解还不够深入,所以在这个方面我就犯了个错误. 希望本文能把我犯的这个错误阐述清楚, 如果能帮助园里其他朋友避免这个问题当然是最好的了.

早前的设计

先用一张图来描述早前的代码结构

Singleton<T>:是一个单例的基类, 用来实现单例模式.

Base<T>:则是一个基础类,它有一些静态的属性和方法(例如访问Redis,kafka,数据库等). 这些属性和方法提供给 Child1 和 Child2 去使用.

Child1 和Child2: 相当于不同模块的业务逻辑实现.

我期望的结果是Base<T>里面的静态成员在整个程序运行期间只有一份.

代码的实现

Singleton

    public abstract class Singleton<T> where T : new()
{
/// <summary>
/// 锁定对象
/// </summary>
private static readonly object locker = new object();
/// <summary>
/// T 的实例
/// </summary>
static T instance = default(T);
/// <summary>
/// T 的实例
/// </summary>
public static T Instance
{
get
{
if (null == instance)
{
lock (locker)
{
if (null == instance)
{
instance = new T();
}
}
}
return instance;
}
}
}

Base

public class Base<T> : Singleton<T> where T : new()
{
protected static object Object { set; get; } static Base()
{
Object = new object();
}
}

Child1 和Child2

public class Child1 : Base<Child1>
{
} public class Child2 : Base<Child2>
{
}

我以为 Base的静态构造函数只会执行一次. 可是当我在程序里使用 Child1.Instance 和 Child2.Instance 时发现, Base的静态构造函数被执行了2次. 那么Child1.Instance的Object和Child2.Instance的Object对象一定不是同一个.

那么问题出现在什么地方了呢? 答案其实挺简单的:系统认为 Base<Child1> 和 Base<Child2>并不相同. 相当于在系统里定义了Base_Child1 和Base_Child2两个类. 如果我们这么理解这个问题 ,那么Base的静态构造函数被执行了2次就不难理解了.(我觉得我已经把这个问题的成因描述清楚了,如果您没理解,欢迎在下面评论.)

如果要达到我设计的目标应该怎么做呢?

修正的设计

还是先上类图.

Base:

public class Base
{
protected static object Object { set; get; } static Base()
{
Object = new object();
}
}

Singleton:

public abstract class Singleton<T>: Base where T : new()
{
/// <summary>
/// 锁定对象
/// </summary>
private static readonly object locker = new object();
/// <summary>
/// T 的实例
/// </summary>
static T instance = default(T);
/// <summary>
/// T 的实例
/// </summary>
public static T Instance
{
get
{
if (null == instance)
{
lock (locker)
{
if (null == instance)
{
instance = new T();
}
}
}
return instance;
}
}
}

Child1 和Child2:

public class Child1 : Singleton<Child1>
{
} public class Child2 : Singleton<Child2>
{
}

由Singleton 来继承Base.然后Child1 和Child2来继承Singleton. 这样问题就都解决了.

C#泛型设计的一个小陷阱.的更多相关文章

  1. [需求设计]从一个小需求感受Redis的独特魅力

    分享一个简单的小需求应该怎么设计实现以及有关Redis的使用 Redis在实际应用中使用的非常广泛,本篇文章就从一个简单的需求说起,为你讲述一个需求是如何从头到尾开始做的,又是如何一步步完善的.之前写 ...

  2. 警惕!Unity3D中UnityEngine.Object的一个小陷阱

    先看看如下C#的脚本代码: 猜猜控制台打出来的是什么? In the bool parameter function, value info is:  True 肯定出乎很多人的意料吧? transf ...

  3. Thread与Runnable的一个小陷阱

    Java里面运行一个线程可以通过继承Thread的方式,也可以通过实现Runnable的接口来实现,那么两者能不能混用呢,比如以下的例子: public class JavaTest extends ...

  4. ios调用dismissViewController的一个小陷阱

    我们的APP从启动到进入主页面.是通过presentViewController构造了一个ViewController序列,类似于首页 -> 登陆页 -> 启动载入页 -> 主页面 ...

  5. QString 转换成 wchar 的一个小陷阱

    QString::toWCharArray(wchar_t * array) 其中 wchar_t * array 除了要分配内存之外,必须用 wmemset 初始化. 环境是 Visual Stud ...

  6. 一个小栗子聊聊JAVA泛型基础

    背景 周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点: 日志信息严重丢失,茫茫代码毫无头绪. 对泛型的认识不够,导致代码出现了BUG. 第一个原因可以通过以后编码 ...

  7. T-SQL中的一些小陷阱

    1,当心ISNULL函数对你的逻辑引起BUG 有人喜欢或者习惯于(并不代表我推荐,甚至这种写法没有任何好处)用ISNULL处理变量这种方式写查询 比如:select * from TestISNULL ...

  8. python小练习1:设计这样一个函数,在桌面的文件夹上创建10个文本,以数字给它们命名。

    python小练习1:设计这样一个函数,在桌面的文件夹上创建10个文本,以数字给它们命名. 使用for循环即可实现: for name in range(1,11): desktop_path='C: ...

  9. Go的List操作上的一个小“坑”

    转自http://sharecore.net/blog/2014/01/09/the-trap-in-golang-list/ 一直想不清楚一个问题,简单设计的东西到底是“坑多”还是“坑少”呢? 复杂 ...

随机推荐

  1. c# .NET RSA结合AES加密服务端和客户端请求数据

    这几天空闲时间就想研究一下加密,环境是web程序,通过js请求后台返回数据,我想做的事js在发送请求前将数据加密,服务端收到后解密,待服务端处理完请求后,将处理结果加密返回给客户端,客户端在解密,于是 ...

  2. iOS 设置 (plist)

    Architectures:

  3. Android intent 传值不更新的原因和解决办法

    当 Activity 的启动模式是 singleTask 或者 singleInstance 的时候.如果使用了 intent 传值,则可能出现 intent 的值无法更新的问题.也就是说每次 int ...

  4. kubectl get componentstatus ERROR:HTTP probe failed with statuscode: 503

    通过kubectl命令可以查看k8s各组件的状态: [root@wecloud-test-k8s-1 ~]# kubectl get cs NAME STATUS MESSAGE ERROR cont ...

  5. 有效单词词广场——算法面试刷题5(for google),考察数学

    给定一个单词序列,检查它是否构成一个有效单词广场.一个有效的单词广场应满足以下条件:对于满足0≤k<max(numRows numColumns)的k,第k行和第k列对应的字符串应该相同,. 给 ...

  6. “全栈2019”Java异常第十七章:Error详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  7. kali linux之msf后渗透阶段

    已经获得目标操作系统控制权后扩大战果 提权 信息收集 渗透内网 永久后门 基于已有session扩大战果 绕过UAC限制 use exploit/windows/local/ask set sessi ...

  8. LOJ#6045. 「雅礼集训 2017 Day8」价(最小割)

    题面 传送门 题解 首先先把所有权值取个相反数来求最大收益,因为最小收益很奇怪 然后建图如下:\(S\to\)药,容量\(\inf+p_i\),药\(\to\)药材,容量\(\inf\),药材\(\t ...

  9. 网卡NAT方式下虚拟机安装FTP服务

    在windows8下安装Oracle VM VirtualBox虚拟机,虚拟机中安装的CentOS操作系统,在CentOS中搭建LNMP环境,安装vsftpd服务器,宿主机在phpStorm编程,将代 ...

  10. samba服务器的安装与配置(之前是因为没有把共享文件权限放开!)(windows7和centOS6)

    注:centOS6跟centOS7安装方法不一样哦.以下是centOS6的安装过程. 安装:yum -y install samba 启动:/etc/init.d/smb start 设置开机启动:c ...