MemoryCache 的原生插值方式淺談

大雄45發表於2022-12-12
導讀 這在Github上也有issue討論,從2017年開始就有大佬質疑這是一個反人類的設計思路,官方為了不引入Break Change,一直保持到現在。

.NET執行時內建了常用的快取模組:MemoryCache
MemoryCache 的原生插值方式淺談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 的原生插值方式淺談MemoryCache 的原生插值方式淺談
是不是很意外。

但是看官們一般不會使用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);

如此便能正確輸出。
MemoryCache 的原生插值方式淺談MemoryCache 的原生插值方式淺談
擴充套件類原始碼看一看

public static TItem Set(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

綜上:快取項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

MemoryCache插值的實現過程很奇葩
儘量使用帶明確大括號範圍的using語法,C#8.0推出的不帶大括號的using語法糖的作用時刻在函式末尾,會帶來誤導。

原文來自:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2852454/,如需轉載,請註明出處,否則將追究法律責任。

相關文章