当前位置: 首页 > news >正文

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

这里我想说的是类型“实例”的缓存,适用于那些实例或者值计算很耗时的操作。典型的场景如反射获取 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.com,walterlv/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://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

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

相关文章:

  • 使用 Postman 调试 ASP.NET Core 开发的 API
  • 只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式)
  • UWP 中的各种文件路径(用户、缓存、漫游、安装……)
  • 使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(解析篇)
  • 使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(概念篇)
  • csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的?
  • C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
  • WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)
  • 在 Visual Studio Code 中为代码片段(Code Snippets)添加快捷键
  • 在 Visual Studio 中使用 EditorConfig 统一代码风格(含原生与插件)
  • 在 Visual Studio Code 中添加自定义的代码片段
  • 用 dotTrace 进行性能分析时,Timeline 打不开?无法启动进程?也许你需要先开启系统性能计数器的访问权限
  • 了解 .NET/C# 程序集的加载时机,以便优化程序启动性能
  • git 如何更可靠地解决冲突?
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • 【翻译】babel对TC39装饰器草案的实现
  • 4个实用的微服务测试策略
  • Android交互
  • canvas 绘制双线技巧
  • centos安装java运行环境jdk+tomcat
  • es的写入过程
  • gops —— Go 程序诊断分析工具
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • Web标准制定过程
  • 当SetTimeout遇到了字符串
  • 关于Java中分层中遇到的一些问题
  • 计算机在识别图像时“看到”了什么?
  • 前端性能优化——回流与重绘
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • Nginx实现动静分离
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • (bean配置类的注解开发)学习Spring的第十三天
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (十三)Flask之特殊装饰器详解
  • (实战篇)如何缓存数据
  • (原)本想说脏话,奈何已放下
  • (转) RFS+AutoItLibrary测试web对话框
  • **CI中自动类加载的用法总结
  • .form文件_SSM框架文件上传篇
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .Net Remoting常用部署结构
  • .Net 高效开发之不可错过的实用工具
  • .Net的DataSet直接与SQL2005交互
  • .net反编译工具