.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);
会输出如下结果:
是不是很意外。
但是看官们一样平常不会利用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);
如此便能精确输出。
扩展类源码看一看
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 != && CanPropagateOptions()) { PropagateOptions(_previous); } } _previous = ; // 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
时,缓存项实在还没插入!
!
!