.NET Dictionary:获取或创建新的

Rok*_*iša 37 .net c# dictionary insert-update

我经常发现自己创建了Dictionary一个非平凡的值类(例如List),然后在填充数据时总是编写相同的代码模式.

例如:

var dict = new Dictionary<string, List<string>>();
string key = "foo";
string aValueForKey = "bar";
Run Code Online (Sandbox Code Playgroud)

也就是说,我想插入"bar"与key对应的列表"foo",其中key "foo"可能不会映射到任何内容.

这是我使用不断重复的模式的地方:

List<string> keyValues;
if (!dict.TryGetValue(key, out keyValues))
  dict.Add(key, keyValues = new List<string>());
keyValues.Add(aValueForKey);
Run Code Online (Sandbox Code Playgroud)

有更优雅的方式吗?

相关问题没有这个问题的答案:

Ada*_*rth 44

我们对此略有不同,但效果类似:

public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) 
    where TValue : new()
{
    TValue val;

    if (!dict.TryGetValue(key, out val))
    {
        val = new TValue();
        dict.Add(key, val);
    }

    return val;
}
Run Code Online (Sandbox Code Playgroud)

所谓的:

var dictionary = new Dictionary<string, List<int>>();

List<int> numbers = dictionary.GetOrCreate("key");
Run Code Online (Sandbox Code Playgroud)

它利用了公共无参数构造函数的泛型约束:where TValue : new().

为了帮助发现,除非扩展方法非常特定于狭窄的问题,否则我们倾向于将扩展方法放在它们扩展的类型的命名空间中,在这种情况下:

namespace System.Collections.Generic
Run Code Online (Sandbox Code Playgroud)

大多数情况下,使用该类型的人员using在顶部定义了语句,因此IntelliSense也会在代码中找到它所定义的扩展方法.

  • @Darthenius嗯,我刚回到我们的代码并注释掉了我们的`List <>`版本,它不再有用,它在功能上与上面的标准版本相同.所以我删除了它.当我第一次编码它时,它一定是意外落后的. (2认同)

Ale*_*lex 7

如果您使用 .Net Core,则可以使用Dictionary<>.TryAdd().

var dict = new Dictionary<string, string>();
dict.TryAdd("foo", "bar"); // returns bool whether it added or not feel free to ignore.
var myValue = dict["foo"];
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 6

与许多编程问题一样,当您发现自己做了很多事情时,请将其重构为一个方法:

public static void MyAdd<TKey, TCollection, TValue>(
    this Dictionary<TKey, TCollection> dictionary, TKey key, TValue value)
    where TCollection : ICollection<TValue>, new()
{
    TCollection collection;
    if (!dictionary.TryGetValue(key, out collection))
    {
        collection = new TCollection();
        dictionary.Add(key, collection);
    }
    collection.Add(value);
}
Run Code Online (Sandbox Code Playgroud)

  • @Darthenius,如果您愿意,您也可以返回集合,假设这是一件特别常见的事情。根据我使用“Dictionary&lt;TKey, SomeCollection&gt;”模式的经验,我不会经常一次为同一个键添加多个值。如果这种情况经常发生,您可以添加另一个重载,其中接受“IEnumerable&lt;TValue&gt;”,然后将它们全部添加到方法中。 (2认同)

Hei*_*koG 6

这是构造函数需要参数时的解决方案。

public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue> createNew)
    {
        if (!dict.TryGetValue(key, out var val))
        {
            val = createNew();
            dict.Add(key, val);
        }

        return val;
    }
Run Code Online (Sandbox Code Playgroud)

使用简单:

MyDict.GetOrCreate(si.Id, createNew: () => new ObjectKnowingItsId(si.Id))
Run Code Online (Sandbox Code Playgroud)


som*_*men 5

对于更多的读者,这里有一些我认为合适的每种风格的扩展。out如果您需要检查是否添加了值,您也可以使用参数执行某些操作,但我认为您可以使用containskey或已经用于此目的的操作。

您可以用来GetOrAddNew检索项目,或创建项目并将其添加到字典中。您可以使用 的各种重载GetOrAdd来添加新值。这可能是default例如NULL或,0但您也可以提供一个 lambda 来为您构造一个对象,并使用您想要的任何类型的构造函数参数。

var x = new Dictionary<string, int>();
var val = x.GetOrAdd("MyKey", (dict, key) => dict.Count + 2);
var val2 = x.GetOrAdd("MyKey", () => Convert.ToInt32("2"));
var val3 = x.GetOrAdd("MyKey", 1);
Run Code Online (Sandbox Code Playgroud)
    /// <summary>
    /// Extensions for dealing with <see cref="Dictionary{TKey,TValue}"/>
    /// </summary>
    public static class DictionaryExtensions
    {
        public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) 
            where TValue : new() 
            => dict.GetOrAdd(key, (values, innerKey) => EqualityComparer<TValue>.Default.Equals(default(TValue), defaultValue) ? new TValue() : defaultValue);

        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default)
            => dict.GetOrAdd(key, (values, innerKey) => defaultValue);

        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue> valueProvider)
            => dict.GetOrAdd(key, (values, innerKey) => valueProvider());

        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueProvider)
            => dict.GetOrAdd(key, (values, innerKey) => valueProvider(key));

        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<IDictionary<TKey, TValue>, TKey, TValue> valueProvider)
        {
            if (dict == null) throw new ArgumentNullException(nameof(dict));
            if (key == null) throw new ArgumentNullException(nameof(key));
            if (valueProvider == null) throw new ArgumentNullException(nameof(valueProvider));

            if (dict.TryGetValue(key, out var foundValue))
                return foundValue;

            dict[key] = valueProvider(dict, key);
            return dict[key];
        }
    }
Run Code Online (Sandbox Code Playgroud)