我正在尝试创建一个用于循环的循环,该循环在字典中调用了几个类别A
实例的函数,如果没有键的值,则可以创建它然后调用它。在我看来,似乎必须有一种在首次访问钥匙时创建值的方法。
我目前正在使用此代码,尽管我认为这不是最好的做法:
(dictionary[i] = dictionary.ContainsKey(arr[i]) ? dictionary[i] : new A()).Push(10);
在C#?
ConcurrentDictionary
具有GetOrAdd
方法(以及其他有用的方法,例如AddOrUpdate
,TryRemove
等)。如果只是普通词典有 GetOrAdd
,则可以使用...
幸运的是,您可以在静态类中创建一个扩展方法,您可能应该将其命名DictionaryExtensions
:
public static TValue GetOrAdd<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
Func<TKey, TValue> valueFactory)
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
if (key == null)
throw new ArgumentNullException(nameof(key));
if (valueFactory == null)
throw new ArgumentNullException(nameof(valueFactory));
if (dictionary.TryGetValue(key, out var existingValue))
return existingValue;
var value = valueFactory(key);
dictionary.Add(key, value);
return value;
}
如何使用它:
dictionary.GetOrAdd(i, () => new A()).Push(10);
此版本使用一个值工厂,因此仅在需要时执行new A()
。另一个ConcurrentDictionary.GetOrAdd()
Overload使用提供的值作为参数,您可以将其视为替代方案。
我发现创建类似的扩展方法与ConcurrentDictionary
上的方法紧密相吻合非常有用。
我会说清洁器代码看起来像这样:
var key = arr[i];
var hasKey = dictionary.ContainsKey(key);
if (!hasKey)
dictionary.Add(key, new A());
var itemToUse = dictionary[key];
itemToUse.Push(10);
尽管在我看来,您正在寻找 shorter 的东西。我想您真正问的是一种做短途的方法:
如果存在键,将返回给定密钥的值,否则将键添加到具有默认值的字典中。
我认为上面的代码可以说明更多有关意图的信息,但是如果您想要不同的东西,我可以考虑以下两个解决方案。
第一个是获取该项目的扩展方法:
public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
var hasKey = dictionary.ContainsKey(key);
if (!hasKey)
dictionary.Add(key, defaultValue);
return dictionary[key];
}
您将使用它为:
dict.Get(arr[i], defaultValue: new A())
.Push(10);
我能想到的第二个解决方案是字典的新衍生物:
class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
private readonly Func<TKey, TValue> _defaultValueFactory;
public DefaultDictionary(TValue defaultValue)
{
_defaultValueFactory = new Func<TKey, TValue>(x => defaultValue);
}
public DefaultDictionary(Func<TValue> defaultValueFactory)
{
_defaultValueFactory = new Func<TKey, TValue>(x => defaultValueFactory()) ?? throw new ArgumentNullException(nameof(defaultValueFactory));
}
public DefaultDictionary(Func<TKey, TValue> defaultValueFactory)
{
_defaultValueFactory = defaultValueFactory ?? throw new ArgumentNullException(nameof(defaultValueFactory));
}
public new TValue this[TKey key]
{
get
{
var hasKey = ContainsKey(key);
if (!hasKey)
{
var defaultValue = _defaultValueFactory(key);
Add(key, defaultValue);
}
return base[key];
}
set
{
base[key] = value;
}
}
}
使用的用法就像:
var dictionary = new DefaultDictionary<string, A>(() => new A());
// ...
dictionary[arr[i]].Push(10);
我必须警告您一些东西,这个字典的衍生物隐藏了索引操作员。而且,由于使用IDictionary作为成员类型是一种常见的实践(例如private IDictionary<string, A> dictionary
作为成员),因此您不能不铸造而使用过载版本。因此,每次您要使用过载索引器时,要么将变量施放为默认数字,要么具有以下新字典的接口:
interface IDefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
new TValue this[TKey key] { get; set; }
}
让您的会员使用它作为定义类型:
private IDefaultDictionary<string, A> dictionary;
,但这也意味着作为具体类,您现在必须使用默认数字,这就是权衡。