浅谈 MemoryCache 的原生插值方式

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

[[443154]]

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

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

  1. public int Count { get; } 
  2. public void Compact(double percentage); 
  3. public ICacheEntry CreateEntry(object key); 
  4. public void Dispose(); 
  5. public void Remove(object key); 
  6. public bool TryGetValue(object keyout object result); 
  7. protected virtual void Dispose(bool disposing); 

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

就如下这样的常规代码:

  1. var s = new MemoryCache(new MemoryCacheOptions { }); 
  2. var entry = s.CreateEntry("WeChatID"); 
  3. entry.Value = "精益码农"
  4.  
  5. var f =  s.TryGetValue("WeChatID",out  object obj); 
  6.  
  7. Console.WriteLine(f); 
  8. Console.WriteLine(obj); 

会输出如下结果:

是不是很意外。

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

  1. var s = new MemoryCache(new MemoryCacheOptions { }); 
  2. s.Set("WeChatID""精益码农"); 
  3. var f = s.TryGetValue("WeChatID"out object obj); 
  4.  
  5. Console.WriteLine(f); 
  6. Console.WriteLine(obj); 

如此便能正确输出。

扩展类源码看一看

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

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

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

  1. public void Dispose() 
  2.  { 
  3.      if (!_state.IsDisposed) 
  4.      { 
  5.          _state.IsDisposed = true
  6.  
  7.          if (_cache.TrackLinkedCacheEntries) 
  8.          { 
  9.              CacheEntryHelper.ExitScope(this, _previous); 
  10.          } 
  11.  
  12.          // Don't commit or propagate options if the CacheEntry Value was never set
  13.          // We assume an exception occurred causing the caller to not set the Value successfully, 
  14.          // so don't use this entry. 
  15.          if (_state.IsValueSet) 
  16.          { 
  17.              _cache.SetEntry(this); 
  18.  
  19.              if (_previous != null && CanPropagateOptions()) 
  20.              { 
  21.                  PropagateOptions(_previous); 
  22.              } 
  23.          } 
  24.  
  25.          _previous = null; // we don't want to root unnecessary objects 
  26.      } 
  27.  } 

注意其中的_cache.SetEntry(this),表示在MemoryCache底层的ConcurrentDictionary

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

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

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

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

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

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

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

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

  1. using var entry = s.CreateEntry("WeChatID"); 
  2. entry.Value = "精益码农"
  3.             
  4. var f = s.TryGetValue("WeChatID"out object obj); 
  5. ... 

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

Last

 

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

 

责任编辑:武晓燕 来源: 精益码农
相关推荐

2023-09-20 16:11:32

云原生分布式系统

2023-10-26 08:47:30

云原生数据采集

2021-07-14 09:18:19

Python插值算法

2022-09-20 08:00:32

VMWARE云原生

2011-06-20 10:36:29

SEO

2021-07-19 10:43:43

云原生软件开发架构

2009-09-28 13:39:01

Hibernate工作

2012-11-15 13:52:07

系统密码加密加密系统安全

2009-07-14 12:47:07

WebWork工作方式

2012-11-30 14:35:17

2009-09-29 16:16:58

Hibernate H

2020-12-14 15:28:05

云计算架构云原生

2022-05-25 16:32:36

云原生Cloud

2023-11-13 09:28:20

跨组件组件化

2023-08-07 15:49:59

CSS颜色插值算法

2014-12-23 09:57:29

配线架

2011-02-28 11:19:12

双绞线布线

2011-05-10 17:11:46

PR值

2009-07-03 13:24:56

JSP表单

2009-09-11 10:01:57

Linq对象初始值
点赞
收藏

51CTO技术栈公众号