.NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)

.NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)

发布于 2018-09-02 14:27 更新于 2018-09-02 07:59

这里我想说的是类型“实例”的缓存,适用于那些实例或者值计算很耗时的操作。典型的场景如反射获取 Attribute


适用

本文推荐的方法适用于相同的输入可以获得相同的输出,但是这个输入到输出的过程非常耗时。

大家都知道反射是很耗时的,尤其是获取 Attribute 和反射调用实例的方法。而从一个反射的成员中得到其 Attribute 是唯一的输入对应唯一的输出。

思路

既然唯一的输入对应唯一的输出,那么我们可以通过一个字典来储存我们已经转换过的输出。

// 其中 TSource 表示输入的类型,TCache 表示输出的类型。
Dictionary<TSource, TCache> _cacheDictionary = new Dictionary<TSource, TCache>();

然后我们把已经计算过输出的输入存入到这个字典中。这样,当我们试图重新计算相同输入的输出的时候,便可以直接从字典中取得所需的输出的值。

为了通用一点,我设计一个类型 CachePool<TSource, TCache>

namespace Walterlv
{
    public sealed class CachePool<TSource, TCache>
    {
        Dictionary<TSource, TCache> _cacheDictionary = new Dictionary<TSource, TCache>();

        private TCache GetOrCacheValue(TSource source)
        {
            // 从这里计算新值或者从字典中获取已经计算的值。
        }
    }
}

这个计算过程是唯一确定的,所以我们可以从构造函数中传入并储存下来。

public CachePool([NotNull] Func<TSource, TCache> conversion)
{
    _convert = conversion ?? throw new ArgumentNullException(nameof(conversion));
}

private readonly Func<TSource, TCache> _convert;

于是我们的缓存类已经近乎完成。为了线程安全,我加了锁;但考虑到部分情况下性能更重要,所以我把锁设为了可选项。

代码

代码我放到了 gist.github.comwalterlv/CachePool.cs

你可以直接点击以上链接查看。为了不影响本文的阅读,我把实际的代码放到了最后。

用法

高性能创建对象

比如你认为反射创建对象是一个耗时的操作,那么可以将构造函数的调用创建成一个委托,然后把这个委托缓存下来。这样,下次创建相同对象的时候就不需要反射调用构造函数了,而是直接调用委托拿到对象的新实例。

private static readonly CachePool<Type, Func<object>> ConstructorCache =
    new CachePool<Type, Func<object>>(x =>
        Expression.Lambda<Func<object>>(Expression.New(x)).Compile());

高性能为属性赋值

我在 如何快速编写和调试 Emit 生成 IL 的代码 一文中创建了可以为属性赋值的委托,你也可以使用此方法将委托缓存下来,以便每次给相同类型的相同属性赋值时能有不那么差的性能。

高性能“反射”调用函数

调用函数所得的结果可是不一样的,所以直接缓存函数结果是不靠谱的,不过我们依然可以将反射调用缓存为委托的调用。我在 .NET Core/Framework 创建委托以大幅度提高反射调用的性能 一文中有介绍。

附代码

本文会经常更新,请阅读原文: https://walterlv.com/post/design-a-cache-pool.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏达摩兵的技术空间

js代码优化日常001

本文开始针对项目中总结出来的关于js基础知识的代码优化技巧进行每个细节点的分析,后续还会针对某个专题的分析。

1773
来自专栏锦小年的博客

python学习笔记7.4-内建模块base64

有时候,我们用noepad++或者记事本打开图片或者程序等文件的时候会显示大量的乱码,主要原因是这些文件编码的时候并不是字符串编码的。如果我们想把这些文件正常显...

2199
来自专栏java一日一条

10个惊艳的Swift单行代码

几年前,一篇表述“10个Scala函数式单行代码”的文章非常受欢迎,并且随后立马出现了其他的语言版本,例如Haskell版本,Ruby版本,Groovy版本,C...

932
来自专栏做全栈攻城狮

程序员带你十天快速入门Python,玩转电脑软件开发(四)

本系列文章立志于从一个已经习得一门编程语言的基础之上,全面介绍Python的相关开发过程和相关经验总结。本篇文章主要是基于上一篇的程序员带你十天快速入门Pyth...

892
来自专栏咸鱼不闲

fastjson 重复引用和循环引用问题

数据传输使用json格式再方便不过了。 fastjson 由阿里巴巴那伙人使用Java语言编写,号称最快的JSON库 前两天遇到一个问题 后台的数据转化为jso...

2734
来自专栏java达人

Java中的堆和栈的区别

当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更...

2296
来自专栏blackheart的专栏

[程序设计语言]-[核心概念]-02:名字、作用域和约束(Bindings)

1.名字、约束时间(Binding Time) 在本篇博文开始前先介绍两个约定:第一个是“对象”,除非在介绍面向对象语言时,本系列中出现的对象均是指任何可以有名...

2258
来自专栏项勇

笔记45 | 代码性能优化建议[转]

1376
来自专栏gaoqin31

设计模式之 工厂模式

简单工厂模式 : 简单工厂模式是属于创建型的设计模式,又叫做静态工厂方法模式,但不属于23种GOF设计模式,简单工厂模式是由一个工厂决定创建哪一类产品的实例,简...

1555
来自专栏逸鹏说道

C# 温故而知新:Stream篇(五)上

MemoryStream 目录: 1 简单介绍一下MemoryStream 2 MemoryStream和FileStream的区别 3 通过部分源码深入了解下...

4055

扫码关注云+社区

领取腾讯云代金券