【问题标题】:Combined "Check Add or Fetch" from Dictionary结合字典中的“检查添加或获取”
【发布时间】:2010-09-14 03:36:19
【问题描述】:

我厌倦了这个字典成语:

        Dictionary<Guid,Contact> Contacts;
        //...
        if (!Contacts.ContainsKey(id))
        {
            contact = new Contact();
            Contacts[id] = contact;
        }
        else
        {
            contact = Contacts[id];
        }

如果有一种语法允许从默认构造函数隐式创建新值,如果它不存在(字典知道值的类型,毕竟)。有人见过这样做的助手(例如扩展方法)吗?

【问题讨论】:

    标签: c# collections syntax dictionary extension-methods


    【解决方案1】:

    实施:

    public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,
                                                TKey key, Func<TValue> valueCreator)
    {
        TValue value;
        if (!dictionary.TryGetValue(key, out value))
        {
            value = valueCreator();
            dictionary.Add(key, value);
        }
        return value;
    }
    
    public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,
                                                TKey key) where TValue : new()
    {
       return dictionary.GetOrAdd(key, () => new TValue());
    }
    

    用法:

    var contacts = new Dictionary<Guid, Contact>();
    Guid id = ...
    
    contacts.GetOrAdd(id).Name = "Abc"; // ok since Contact has public parameterless ctor
    contacts.GetOrAdd(id, () => new Contact { Name = "John Doe" }).Age = 40;
    

    【讨论】:

      【解决方案2】:

      与 Ani 的答案相同,但更难以理解:)

      /// <param name="valueCreator">The expensive value creator function.</param>
      public static T GetOrAdd<S, T>(this IDictionary<S, T> dict, S key, Func<T> valueCreator)
      {
          return dict.TryGetValue(key, out var value) ? value : dict[key] = valueCreator();
      }
      

      提供一个委托作为值创建者而不是值本身,以防止不必要的对象创建。

      字典,很遗憾,doesn't have this feature out of the box to do all this in a single lookup

      【讨论】:

      • 只是关于您提到使用价值创造者的说明。当创建价值昂贵或常见情况不是创建新实例时,这更有意义。如果您只是使用值类型或通过分析您发现常见情况是创建新实例,这可能会比必要的开销更大,因此最好同时支持直接值分配和泛型委托。在某些情况下,您希望分配已创建的值。
      • 这个答案是错误的——优先级是 !: before =,所以 valueCreator 总是被执行。在作业周围加上括号。
      • @WarwickAllison,我不确定新版本的 C# 是否有所改变,但该代码在我测试的地方可以正常工作。 valueCreator 并不总是被执行。证明:dotnetfiddle.net/NiWdt7。但是,是的,代码可读性包括括号。我正在追求一个花哨的单线:)
      【解决方案3】:

      您可以随时编写自己的字典。

      解决方案 1:继承并使用“新”覆盖方法首先检查包含键。如果是,则通过键返回该值或通过 a

      创建
      Func<K, T>
      

      代表。但是,通过界面使用此字典时,此解决方案将中断

      IDictionary<K,T>
      

      因此,您需要通过解决方案 2 更加彻底。

      解决方案 2:使用内部字典的 Dictionary Wrapper - 其余与解决方案 1 相同。

      解决方案 3:ConcurrentDictionary 提供 GetOrAdd,它也是线程安全的。

      解决方案 4:类似于解决方案 2 的 ConcurrentDictionary Wrapper。

      这是一个字典包装器:

      public class WrappedDictionary<K, T> : IDictionary<K, T>
      {
          public IDictionary<K, T> WrappedInstance { get; set; }
      
          public virtual T this[K key]
          {
              get
              {
                  // CUSTOM RESOLUTION CODE GOES HERE
                  return this.WrappedInstance[key];
              }
              set
              {
                  this.WrappedInstance[key] = value;
              }
          }
      
          public int Count
          {
              get
              {
                  return this.WrappedInstance.Count;
              }
          }
      
          public bool IsReadOnly
          {
              get
              {
                  return this.WrappedInstance.IsReadOnly;
              }
          }
      
          public ICollection<K> Keys
          {
              get
              {
                  return this.WrappedInstance.Keys;
              }
          }
      
          public ICollection<T> Values
          {
              get
              {
                  return this.WrappedInstance.Values;
              }
          }
      
          public void Add(KeyValuePair<K, T> item)
          {
              this.WrappedInstance.Add(item);
          }
      
          public void Add(K key, T value)
          {
              this.WrappedInstance.Add(key, value);
          }
      
          public void Clear()
          {
              this.WrappedInstance.Clear();
          }
      
          public bool Contains(KeyValuePair<K, T> item)
          {
              return this.WrappedInstance.Contains(item);
          }
      
          public bool ContainsKey(K key)
          {
              return this.WrappedInstance.ContainsKey(key);
          }
      
          public void CopyTo(KeyValuePair<K, T>[] array, int arrayIndex)
          {
              this.WrappedInstance.CopyTo(array, arrayIndex);
          }
      
          public IEnumerator<KeyValuePair<K, T>> GetEnumerator()
          {
              return this.WrappedInstance.GetEnumerator();
          }
      
          public bool Remove(KeyValuePair<K, T> item)
          {
              return this.WrappedInstance.Remove(item);
          }
      
          public bool Remove(K key)
          {
              return this.WrappedInstance.Remove(key);
          }
      
          public bool TryGetValue(K key, out T value)
          {
              // CUSTOM RESOLUTION CODE GOES HERE
              return this.WrappedInstance.TryGetValue(key, out value);
          }
      
          IEnumerator IEnumerable.GetEnumerator()
          {
              return this.WrappedInstance.GetEnumerator();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-07
        • 1970-01-01
        • 2019-07-21
        • 1970-01-01
        相关资源
        最近更新 更多