C# 在字典和集合上自动添加索引器是一个好的设计决策吗?
索引器何时可以自动将项目添加到集合/词典?这是合理的,还是与最佳实践相反C# 在字典和集合上自动添加索引器是一个好的设计决策吗?,c#,collections,dictionary,indexing,C#,Collections,Dictionary,Indexing,索引器何时可以自动将项目添加到集合/词典?这是合理的,还是与最佳实践相反 public class I { /* snip */ } public class D : Dictionary<string, I> { public I this[string name] { get { I item; if (!this.TryGetValue(name, out item))
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;
}
}
}
公共类I{/*snip*/}
公共D类:字典
{
公共I此[字符串名称]
{
得到
{
I项目;
如果(!this.TryGetValue(名称,out项))
{
项目=新的I();
添加(名称、项目);
}
退货项目;
}
}
}
如何在集合中使用此选项的示例:
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)
}
公共一级
{
公共I(字符串名){/*snip*/}
公共字符串名称{get;private set;}
/*剪断*/
}
公共C类:收藏
{
私有字典名称索引=新字典();
公共I此[字符串名称]
{
得到
{
I项目;
如果(!nameIndex.TryGetValue(名称,输出项))
{
项目=新I(名称);
this.Add(item);//还将该项添加到nameIndex
}
退货项目;
}
}
////Snip:管理名称索引的代码
//受保护的覆盖无效ClearItems()
//受保护的覆盖无效插入项(int索引,I项)
//受保护的覆盖void removietem(int索引)
//受保护的覆盖无效集合项(int索引,I项)
}
在没有其他关于你正在做什么的信息的情况下,我觉得这是一种令人惊讶的行为。我希望您能从上下文(即,将其命名为autoinitializedictionary
或其他东西)中清楚地说明所期望的内容
我个人更愿意将其作为一种方法,而不是索引器;类似于
D.FindOrCreate
。(我觉得有一个惯用的名称来表示我暂时忘记了的方法。)我认为只要这种行为被完全清楚地表达出来,它是完全好的。我有两个装饰师课程:
public class DefaultValueDictionary<K, V> : IDictionary<K, V>
{
public DefaultValueDictionary(IDictionary<K, V> baseDictionary, Func<K, V> defaultValueFunc)
{
...
}
}
我认为这违反了两个原则。1) 最小意外原则。2)getter不应该改变任何事情 如果集合中不存在foo,我不希望在对{“foo”,null}中添加一个
x = collection["Foo"]
<>强>有两个问题你应该考虑——这两个问题都表明这是个坏主意。< /强> 首先,从.NET BCL集合类型继承通常不是一个好主意。这样做的主要原因是这些类型上的大多数方法(如
Add
和Remove
)都不是虚拟的——如果您在派生类中提供自己的实现,那么如果您将集合作为基类型传递,它们将不会被调用。在您的例子中,通过隐藏Dictionary
indexer属性,您正在创建一种情况,即使用基类引用的调用将执行与使用派生类引用的调用不同的操作。。。违反以下规定:
什么时候可以接受索引器
将项目自动添加到
收藏/字典
从来没有
这是合理的,还是与此相反
最佳实践
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;
}
}
}
与最佳做法相反
也就是说,如果类的名称恰当,那么它是可以接受的。我个人会使用它。我担心的主要原因是它不会是线程安全的。多个读者都试图一次写入字典,这需要仔细的锁管理,而这在一开始你可能不会想到(或得到正确的答案)。我相信你正在寻找GetOrAdd(key,value)。这里是一个msdn示例,感谢所有反馈!我已经添加了一个示例,说明如何在集合中使用它。虽然到目前为止,实现集合的潜在问题较少,但我不确定它是否真的会更改任何参数。+1:有趣的是,注意到ASP.NET会话会执行类似的操作:如果你来自C++,你已经使用了STD::MAP,那么不增加密钥会违反最小的惊讶原则。这不是黑和白。@冰激凌我相信如果你从C++去C,你会很惊讶。
int count;
if(!dict.TryGetValue(key, out count))
dict[count] = 1;
else dict[count] = count + 1;
x = collection["Foo"]
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!
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;
}
}