You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重写ConcurrentDictionary的GetOrAdd调用以避免闭包分配

问题原因

原写法中GetOrAdd传入的lambda捕获了方法作用域的expiration参数,编译器会生成匿名闭包类,每次调用方法都会产生不必要的堆分配,这就是Rider提示闭包分配问题的来源。

单参数场景(仅传expiration)的重写方案

直接使用你提到的带TArg的GetOrAdd重载,把额外参数通过第三个参数传入,lambda的第二个参数接收这个值,完全避免捕获外部变量:

public static ICacheManager GetCacheManager(string cacheManagerName, TimeSpan? expiration)
{
    var cacheManager = CacheManagers.GetOrAdd(
        key: cacheManagerName,
        valueFactory: static (keyName, exp) =>
        {
            var ret = ServiceLocator.Current.GetInstance<ICacheManager>();
            if (exp.HasValue)
            {
                ret.Configure(keyName, exp.Value);
            }
            else
            {
                ret.Configure(keyName);
            }
            return ret;
        },
        factoryArgument: expiration
    );

    return cacheManager;
}

C# 9及以上版本可以给lambda加static修饰,强制编译器检查lambda是否捕获了外部变量,从语法层面避免不小心写出闭包代码,无兼容需求时推荐加上。

多参数场景的处理方案

如果需要传递多个额外参数,直接用值元组(ValueTuple) 作为TArg即可,值元组是值类型,不会产生额外堆分配:

// 示例场景:需要额外传递数据库连接字符串、查询超时时间两个参数
public static ICacheManager GetCacheManager(string cacheManagerName, TimeSpan? expiration, string dbConnStr, int queryTimeout)
{
    var cacheManager = CacheManagers.GetOrAdd(
        key: cacheManagerName,
        valueFactory: static (keyName, args) =>
        {
            var (exp, connStr, timeout) = args;
            var ret = ServiceLocator.Current.GetInstance<ICacheManager>();
            if (exp.HasValue)
            {
                ret.Configure(keyName, exp.Value);
            }
            else
            {
                ret.Configure(keyName);
            }
            // 其他逻辑可以直接使用connStr、timeout参数
            return ret;
        },
        // 所有额外参数打包成值元组传入
        factoryArgument: (expiration, dbConnStr, queryTimeout)
    );

    return cacheManager;
}

如果使用的C#版本低于7.0不支持值元组,自定义只读结构体作为参数载体即可,同样不会产生堆分配:

// 自定义参数结构体
public readonly struct CacheFactoryArgs
{
    public TimeSpan? Expiration { get; }
    public string DbConnStr { get; }
    public int QueryTimeout { get; }

    public CacheFactoryArgs(TimeSpan? expiration, string dbConnStr, int queryTimeout)
    {
        Expiration = expiration;
        DbConnStr = dbConnStr;
        QueryTimeout = queryTimeout;
    }
}

// 调用时传入自定义结构体
public static ICacheManager GetCacheManager(string cacheManagerName, TimeSpan? expiration, string dbConnStr, int queryTimeout)
{
    var args = new CacheFactoryArgs(expiration, dbConnStr, queryTimeout);
    var cacheManager = CacheManagers.GetOrAdd(
        cacheManagerName,
        static (keyName, factoryArgs) =>
        {
            var ret = ServiceLocator.Current.GetInstance<ICacheManager>();
            // 直接使用factoryArgs的属性即可
            return ret;
        },
        args
    );
    return cacheManager;
}

内容的提问来源于stack exchange,提问作者Raptor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:06:05