问题描述
索引器何时可以自动将项目添加到集合/字典?这是合理的还是违反最佳做法?
When is it acceptable for an indexer to automatically add items to a collection/dictionary? Is this reasonable, or contrary to best practices?
public class I { /* snip */ }
public class D : Dictionary<string, I>
{
public I this[string name]
{
get
{
I item;
if (!this.TryGetValue(name, out item))
{
item = new I();
this.Add(name, item);
}
return item;
}
}
}
用于集合中:
public class I
{
public I(string name) {/* snip */}
public string Name { get; private set; }
/* snip */
}
public class C : Collection<I>
{
private Dictionary<string, I> nameIndex = new Dictionary<string, I>();
public I this[string name]
{
get
{
I item;
if (!nameIndex.TryGetValue(name, out item))
{
item = new I(name);
this.Add(item); // Will also add the item to nameIndex
}
return item;
}
}
//// Snip: code that manages nameIndex
// protected override void ClearItems()
// protected override void InsertItem(int index, I item)
// protected override void RemoveItem(int index)
// protected override void SetItem(int index, I item)
}
推荐答案
您应该考虑两个问题 - 坏主意。
首先,继承.NET BCL集合类型通常不是一个好主意。这样做的主要原因是这些类型上的大多数方法(如 Add
和 Remove
)不是虚拟的,你在派生类中提供自己的实现,如果你把你的集合作为基类型传递,它们不会被调用。在你的情况下,通过隐藏 Dictionary< TK,TV>
indexer属性,你正在创建一种情况,使用基类引用的调用将做一些不同于调用使用衍生类参考...违反:
First, inheriting from the .NET BCL collection types is not generally a good idea. The main reason for this is that most methods on those types (like Add
and Remove
) are not virtual - and if you provide your own implementations in a derived class, they will not get called if you pass your collection around as the base type. In your case, by hiding the Dictionary<TK,TV>
indexer property, you are creating a situation where a call using a base-class reference will do something different than a call using a derived-class reference ... a violation of the Liskov Substitution Principle:
var derived = new D();
var firstItem = derived["puppy"]; // adds the puppy entry
var base = (Dictionary<string,I>)derived;
var secondItem = base["kitten"]; // kitten WAS NOT added .. BAD!
其次,更重要的是,创建一个索引器,您尝试找到一个完全意外。索引器明确定义 get
和 set
操作 - 实现 get
操作修改集合是非常糟糕。
Second, and more importantly, creating an indexer that inserts an item when you attempt to find one is entirely unexpected. Indexers have clearly defined get
and set
operations - implementing the get
operation to modify the collection is very bad.
对于你描述的情况,你最好创建一个可以操作任何字典的扩展方法。这样的操作在它做什么都不那么令人惊讶,也不需要创建派生集合类型:
For the case you describe, you're much better off creating an extension method that can operate on any dictionary. Such an operation is both less surprising in what it does, and also doesn't require creating a derived collection type:
public static class DictionaryExtensions
{
public static TValue FindOrAdd<TKey,TValue>(
this IDictionary<TKey,TValue> dictionary, TKey key, TValue value )
where TValue : new()
{
TValue value;
if (!this.TryGetValue(key, out value))
{
value = new TValue();
this.Add(key, value);
}
return value;
}
}
这篇关于在字典和集合上自动添加索引器是一个好的设计决策吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!