组合的“检查添加或获取”来自字典

发布于 2024-09-19 18:30:59 字数 388 浏览 9 评论 0原文

我厌倦了这种字典习惯用法:

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

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

I'm tired of this dictionary idiom:

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

It would be nice if there was a syntax that permitted the new value to be created implicitly from a default constructor if it does not exist (the dictionary knows the type of the value, after all). Anyone seen a helper (such as an extension method) that does this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

辞慾 2024-09-26 18:30:59

实现:

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;

Implementation:

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());
}

Usage:

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;
赠意 2024-09-26 18:30:59

与 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();
}

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

字典,不幸的是,没有开箱即用的功能来在一次查找中完成所有这些< /a>.

Same as Ani's answer, but in a more unintelligible one-liner :)

/// <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();
}

Provide a delegate as value creator than value itself to prevent unnecessary object creation.

Dictionary, unfortunately, doesn't have this feature out of the box to do all this in a single lookup.

仅此而已 2024-09-26 18:30:59

您随时可以滚动自己的字典。

解决方案 1:继承并使用“新”重写方法,首先检查是否包含密钥。如果是,则通过键返回该值或由委托创建

Func<K, T>

。但是,当通过接口使用此字典时,此解决方案将中断,

IDictionary<K,T>

因此您需要通过解决方案 2 进行更彻底的解决方案。

解决方案 2:使用内部字典的字典包装器 - 其余部分与解决方案 1 相同。解决

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

解决方案 4:ConcurrentDictionary 包装器类似于解决方案 2。

这是一个字典包装器:

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();
    }
}

You can always roll your own dictionary.

Solution 1: Inherit and use "new" overriding methods checking first for containing key. If yes return that value by key or create by a

Func<K, T>

delegate. However, this solution will break when using this dictionary via the interface

IDictionary<K,T>

So for that you need to be more thorough via solution 2.

Solution 2: Dictionary Wrapper which uses an internal dictionary - the rest is the same as solution 1.

Solution 3: ConcurrentDictionary offers GetOrAdd which is also thread safe.

Solution 4: ConcurrentDictionary Wrapper similar to solution 2.

Here's a dictionary 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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文