如何在.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
相关产品推荐
相关产品推荐

