淺談MemoryCache的原生插值方式

.NET運(yùn)行時(shí)內(nèi)置了常用的緩存模塊:MemoryCache
標(biāo)準(zhǔn)的MemoryCache暴露了如下幾個(gè)屬性和方法:
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);
但是你使用常規(guī)模式去插值/獲取值,可能會(huì)出現(xiàn)意想不到的情況。
就如下這樣的常規(guī)代碼:
var?s?=?new?MemoryCache(new?MemoryCacheOptions?{?});
var?entry?=?s.CreateEntry("WeChatID");
entry.Value?=?"精益碼農(nóng)";
var?f?=??s.TryGetValue("WeChatID",out??object?obj);
Console.WriteLine(f);
Console.WriteLine(obj);
會(huì)輸出如下結(jié)果:
是不是很意外。
但是看官們一般不會(huì)使用MemoryCache的原生方法,而是使用位于同一命名空間的
擴(kuò)展方法Set。
var?s?=?new?MemoryCache(new?MemoryCacheOptions?{?});
s.Set("WeChatID",?"精益碼農(nóng)");
var?f?=?s.TryGetValue("WeChatID",?out?object?obj);
Console.WriteLine(f);
Console.WriteLine(obj);
如此便能正確輸出。
擴(kuò)展類源碼看一看
?public?static?TItem?Set(this?IMemoryCache?cache,?object?key,?TItem?value)
?{
??????using?ICacheEntry?entry?=?cache.CreateEntry(key);
??????entry.Value?=?value;
??????return?value;
}
擴(kuò)展方法與原生方法的差異在于using關(guān)鍵字 (也說明了CacheEntry繼承自IDisposable接口)。
繼續(xù)追溯CacheEntry實(shí)現(xiàn)的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集合插入緩存項(xiàng),
綜上:緩存項(xiàng)CacheEntry需要被Dispose,才能被插入MemoeyCache。
這是怎樣的設(shè)計(jì)模式?IDisposable接口不是用來釋放資源嗎?
為啥要使用Dispose方法來向MemoryCache插值?
不能使用一個(gè)明確的Commit方法嗎?
這在Github上也有issue討論,從2017年開始就有大佬質(zhì)疑這是一個(gè)反人類的設(shè)計(jì)思路,官方為了不引入Break Change,一直保持到現(xiàn)在。
基于此現(xiàn)狀,我們?nèi)绻褂肕emoryCache的原生插值方法, 需要這樣:
?var?s?=?new?MemoryCache(new?MemoryCacheOptions?{?});
?using?(var?entry?=?s.CreateEntry("WeChatID"))
?{
??????entry.Value?=?"精益碼農(nóng)";
?}
?var?f?=?s.TryGetValue("WeChatID",?out?object?obj);
?...
盡量不要使用C#8.0推出的不帶大括號(hào)的using語法
?using?var?entry?=?s.CreateEntry("WeChatID");
?entry.Value?=?"精益碼農(nóng)";
????????????
?var?f?=?s.TryGetValue("WeChatID",?out?object?obj);
?...
這種沒明確指定using作用范圍的語法,會(huì)在函數(shù)末尾才執(zhí)行Dispose方法, 導(dǎo)致執(zhí)行到TryGetValue時(shí),緩存項(xiàng)其實(shí)還沒插入!!!
Last
MemoryCache插值的實(shí)現(xiàn)過程很奇葩 盡量使用帶明確大括號(hào)范圍的using語法,C#8.0推出的不帶大括號(hào)的using語法糖的作用時(shí)刻在函數(shù)末尾,會(huì)帶來誤導(dǎo)。
●HTTP1.1 Keep-Alive到底算不算長(zhǎng)連接?
●SignalR 開發(fā)到生產(chǎn)部署閉坑指南
●SignalR在React/Go技術(shù)棧的實(shí)踐
●我是狀態(tài)機(jī), 一顆永遠(yuǎn)騷動(dòng)的機(jī)器引擎

點(diǎn)個(gè)在看你最好看

