前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >谈谈MemoryCache原生插值方式

谈谈MemoryCache原生插值方式

作者头像
喵叔
发布2021-12-31 08:19:41
2880
发布2021-12-31 08:19:41
举报
文章被收录于专栏:喵叔's 专栏
案例

我们都知道.NET运行时内置了常用缓存模块MemoryCache,它暴露了以下几个属性和方法:

代码语言:javascript
复制
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);

当我们使用常规模式去插值和获取值时很有可能会出现意想不到的问题,例如下的代码:

代码语言:javascript
复制
var mc = new MemoryCache(new MemoryCacheOptions { });
var entry = mc.CreateEntry("MiaoShu");
entry.Value = "喵叔";
var f =  mc.TryGetValue("MiaoShu",out  object obj);
Console.WriteLine(f);
Console.WriteLine(obj);

运行代码后,输出结果如下:

在这里插入图片描述
在这里插入图片描述

看到输出结果是不是很意外,和你想到不一样。从代码中可以看出使用的是MemoryCache原生方法,但一般我们不这么用,而是使用位于同一命名空间的扩展方法 Set,代码如下:

代码语言:javascript
复制
var s = new MemoryCache(new MemoryCacheOptions { });
s.Set("MiaoShu", "喵叔");
var f = s.TryGetValue("MiaoShu", out object obj);
Console.WriteLine(f);
Console.WriteLine(obj);

运行代码后,输出如下:

在这里插入图片描述
在这里插入图片描述
分析

为什么会出现上一小节这种情况呢?下面让我们来看一下Set的源码:

代码语言:javascript
复制
 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方法:

代码语言:javascript
复制
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。WTF?这是什么鬼设计,IDisposable接口不是应该用来释放资源吗?为什么使用Dispose方法来向MemoryCache插值呢?这个问题在2017年开始就有人质疑这个设计,但是官方为了不引入Break Change,一直保持现状到现在。因此根据现状,如果使用MemoryCache的原生插值方法,代码需要这么些:

代码语言:javascript
复制
var s = new MemoryCache(new MemoryCacheOptions { });
using (var entry = s.CreateEntry("MiaoShu"))
{
     entry.Value = "喵叔";
}
var f = s.TryGetValue("MiaoShu", out object obj);

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

代码语言:javascript
复制
using var entry = s.CreateEntry("MiaoShu");
entry.Value = "喵叔"; 
var f = s.TryGetValue("MiaoShu", out object obj);

不带大括号的using语法没明确指定using的作用范围,会在函数末尾才执行Dispose方法,导致执行到TryGetValue时缓存项还没插入。

总结

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例
  • 分析
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档