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

如何在.NET Dictionary中添加元素时仅检查一次键并延迟构建值?

Dictionary<K,V>高效添加+延迟构建值的实现方案

先明确核心诉求:既要避免ContainsKey+Add带来的两次哈希查找,又要实现仅在键不存在时才构建值(避免不必要的对象创建开销)。

现有写法的问题

  • 传统写法if(!dico.ContainsKey(key)) { dico.Add(key, new Value()); }会执行两次哈希查找(ContainsKey一次,Add内部又一次),存在性能冗余。
  • TryAdd(key, new Value())只做一次查找,但会提前创建Value实例,哪怕键已经存在,造成不必要的性能损耗。

可行解决方案

1. .NET 6+ 推荐使用:CollectionsMarshal.GetValueRefOrAddDefault

官方提供的高性能API,仅需一次哈希查找,同时支持延迟构建值:

using System.Runtime.InteropServices;

// 获取值的引用,不存在则添加默认占位(不会创建Value实例)
ref var valueRef = ref CollectionsMarshal.GetValueRefOrAddDefault(dico, key, out bool exists);
if (!exists)
{
    // 仅当键不存在时才构建值
    valueRef = new Value();
}

这个方法直接操作字典的内部哈希结构,性能最优,且完全符合需求。

2. 自定义扩展/包装类(兼容低版本.NET)

如果使用.NET Core 3.0或更低版本,可以自己实现支持工厂方法的添加逻辑:

方式A:自定义包装字典

public class SmartDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public bool TryAddWithFactory(TKey key, Func<TValue> valueFactory)
    {
        // 一次查找判断是否存在
        if (TryGetValue(key, out _))
            return false;
        
        // 仅当需要时构建值并添加
        Add(key, valueFactory());
        return true;
    }
}

// 使用示例
var dico = new SmartDictionary<int, Value>();
dico.TryAddWithFactory(key, () => new Value());

这里TryGetValue只做一次哈希查找,后续Add的内部检查因已确认键不存在,不会触发重复查找的性能问题。

方式B:反射调用内部Insert方法(不推荐,依赖内部实现)

如果追求极致性能,可通过反射调用Dictionary内部的Insert方法(允许跳过存在性检查):

public static class DictionaryExtensions
{
    private static readonly MethodInfo _insertMethod = typeof(Dictionary<,>)
        .GetMethod("Insert", 
                   BindingFlags.Instance | BindingFlags.NonPublic, 
                   new[] { typeof(object), typeof(object), typeof(bool) });

    public static void AddWithoutCheck<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
    {
        _insertMethod
            .MakeGenericMethod(typeof(TKey), typeof(TValue))
            .Invoke(dictionary, new object[] { key, value, false });
    }
}

// 使用示例
if (!dico.ContainsKey(key))
{
    dico.AddWithoutCheck(key, new Value());
}

⚠️ 注意:这种方式依赖.NET的内部实现细节,版本更新可能导致失效,仅在极端性能场景下考虑使用。

内容的提问来源于stack exchange,提问作者Patrick from NDepend team

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 23:33:19