我正在尝试创建一个for循环,它在字典中调用类A
的几个实例的函数,如果没有键的值,它会创建它然后调用它。
在我看来,似乎必须有一种方法来在首次访问密钥时创建一个值。
我目前正在使用此代码,但我认为这不是最好的做法:
(dictionary[i] = dictionary.ContainsKey(arr[i]) ? dictionary[i] : new A()).Push(10);
C#中是否有针对此类问题的清洁工具?
答案 0 :(得分:1)
我说 clean 代码看起来像这样:
var key = arr[i];
var hasKey = dictionary.ContainsKey(key);
if (!hasKey)
dictionary.Add(key, new A());
var itemToUse = dictionary[key];
itemToUse.Push(10);
虽然在我看来你正在寻找更短的。我想你真正要问的是一个简单的方法:
如果密钥存在,则返回给定密钥的值,否则将密钥添加到具有默认值的字典中。
我认为上面的代码更多地讲述了意图,但是如果你想要不同的东西,我可以考虑采用以下两种解决方案。
第一个是获取项目的扩展方法:
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);
我能想到的第二个解决方案是Dictionary的新衍生物:
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);
我必须警告你一些事情,这个Dictionary的派生隐藏了索引操作符。由于使用IDictionary作为成员类型是一种常见做法(例如private IDictionary<string, A> dictionary
作为成员),因此您无法在不进行转换的情况下使用重载版本。因此,每次要使用重载索引器时,要么将变量强制转换为DefaultDictionary,要么为此新字典设置接口,如:
interface IDefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
new TValue this[TKey key] { get; set; }
}
让你的成员,变量将它用作定义类型:
private IDefaultDictionary<string, A> dictionary;
但这也意味着作为一个具体的课程,你现在必须使用DefaultDictionary,这是一种权衡。
答案 1 :(得分:1)
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()
重载使用作为参数提供的值,您可以将其视为替代值。
我发现像这样创建与ConcurrentDictionary
上的方法非常相似的扩展方法非常有用。