浅谈MemoryCache的原生插值方式

简介: MemoryCache插值的实现过程很奇葩尽量使用带明确大括号范围的using语法,C#8.0推出的不带大括号的using语法糖的作用时刻在函数末尾,会带来误导。

.NET运行时内置了常用的缓存模块:MemoryCache


标准的MemoryCache暴露了如下几个属性和方法:


public int Count { get; }
public void Compact(double percentage);
public ICacheEntry CreateEntry(object key);
public void Dispose();
public void Remove(object key);
public bool TryGetValue(object key, out object result);
protected virtual void Dispose(bool disposing);


但是你使用常规模式去插值/获取值,可能会出现意想不到的情况。


就如下这样的常规代码:


var s = new MemoryCache(new MemoryCacheOptions { });
var entry = s.CreateEntry("WeChatID");
entry.Value = "精益码农";
var f =  s.TryGetValue("WeChatID",out  object obj);
Console.WriteLine(f);
Console.WriteLine(obj);


会输出如下结果:


559448af7274364e224017670c27978a.png


是不是很意外。


但是看官们一般不会使用MemoryCache的原生方法,而是使用位于同一命名空间的 扩展方法Set


var s = new MemoryCache(new MemoryCacheOptions { });
s.Set("WeChatID", "精益码农");
var f = s.TryGetValue("WeChatID", out object obj);
Console.WriteLine(f);
Console.WriteLine(obj);


如此便能正确输出。


b30d80d86846d67e04730847679ed329.png


扩展类源码看一看


public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value)
 {
      using ICacheEntry entry = cache.CreateEntry(key);
      entry.Value = value;
      return value;
}


扩展方法与原生方法的差异在于using关键字 (也说明了CacheEntry继承自IDisposable接口)。


继续追溯CacheEntry实现的Dispose方法:


public void Dispose()
        {
            if (!_state.IsDisposed)
            {
                _state.IsDisposed = true;
                if (_cache.TrackLinkedCacheEntries)
                {
                    CacheEntryHelper.ExitScope(this, _previous);
                }
                // Don't commit or propagate options if the CacheEntry Value was never set.
                // We assume an exception occurred causing the caller to not set the Value successfully,
                // so don't use this entry.
                if (_state.IsValueSet)
                {
                    _cache.SetEntry(this);
                    if (_previous != null && CanPropagateOptions())
                    {
                        PropagateOptions(_previous);
                    }
                }
                _previous = null; // we don't want to root unnecessary objects
            }
        }


注意其中的_cache.SetEntry(this),表示在MemoryCache底层的ConcurrentDictionary<object, CacheEntry>集合插入缓存项,


综上:缓存项CacheEntry需要被Dispose,才能被插入MemoeyCache


这是怎样的设计模式?IDisposable接口不是用来释放资源吗?


为啥要使用Dispose方法来向MemoryCache插值?


不能使用一个明确的Commit方法吗?


这在Github上也有issue讨论,从2017年开始就有大佬质疑这是一个反人类的设计思路,官方为了不引入Break Change,一直保持到现在。


基于此现状,我们如果使用MemoryCache的原生插值方法, 需要这样:


var s = new MemoryCache(new MemoryCacheOptions { });
 using (var entry = s.CreateEntry("WeChatID"))
 {
      entry.Value = "精益码农";
 }
 var f = s.TryGetValue("WeChatID", out object obj);
 ...


尽量不要使用C#8.0推出的不带大括号的using语法


using var entry = s.CreateEntry("WeChatID");
 entry.Value = "精益码农";
 var f = s.TryGetValue("WeChatID", out object obj);
 ...


这种没明确指定using作用范围的语法,会在函数末尾才执行Dispose方法, 导致执行到TryGetValue时,缓存项其实还没插入!!!


Last


  1. MemoryCache插值的实现过程很奇葩


  1. 尽量使用带明确大括号范围的using语法,C#8.0推出的不带大括号的using语法糖的作用时刻在函数末尾,会带来误导。
相关文章
|
存储 前端开发 JavaScript
GIS前端编程-Leaflet插件发布
GIS前端编程-Leaflet插件发布
207 0
|
11月前
|
网络协议 数据挖掘 5G
适用于金融和交易应用的低延迟网络:技术、架构与应用
适用于金融和交易应用的低延迟网络:技术、架构与应用
463 5
|
人工智能 运维 监控
云栖实录 |出海新时代:阿里云如何让客户触达更便捷?
阿里云推出场景编排产品Chat Flow,助力中国企业出海
315 2
云栖实录 |出海新时代:阿里云如何让客户触达更便捷?
|
运维 监控 测试技术
5个常见运维场景,用这几个Python脚本就够了!
5个常见运维场景,用这几个Python脚本就够了!
297 0
|
人工智能 安全 数据安全/隐私保护
AIGC内容检测方案初探
【1月更文挑战第15天】AIGC内容检测方案初探
304 1
AIGC内容检测方案初探
|
安全 网络协议 物联网
配置Hotspot2.0无线网络示例
某网络服务商在原有移动网络业务的基础上,新增部署WLAN网络接入业务,为用户提供更好的网络体验。但传统的WLAN网络业务需要用户手动选择SSID,手动接入网络并设置认证信息,用户体验较差。为了提升用户体验,部署Hotspot2.0业务,使用SIM作为用户的身份凭证,让用户无感知的自动接入正确的网络。
234 4
删除有序链表中重复的元素-II(链表)
双指针,slow和fast,并且增加标记flag初始为1。
144 0
|
存储 SQL Oracle
Oracle优化避免索引失效
Oracle优化避免索引失效
541 0
|
Ubuntu 安全 Linux
【Linux命令行艺术】1. 初见命令行
【Linux命令行艺术】1. 初见命令行
|
运维 安全 程序员
纵横“光明顶”,全靠这份全网独一份《数据中心设施运维指南》
数据中心设施运维,经常被与数据中心IT系统运维混为一谈。实际上,两者的工作虽然都以保证IT系统的可用性为最终目标,但在工作对象上,却是截然不同的。IT运维本质上是和比特(bit) 打交道,设施运维则主要和瓦特(Watt)打交道。