字典和收藏上的自动添加索引是否是一个很好的设计决策? [英] Are auto-adding indexers on Dictionaries and Collections a good design decision?

查看:142
本文介绍了字典和收藏上的自动添加索引是否是一个很好的设计决策?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

索引器什么时候可以自动将项目添加到集合/字典?这是否合理,或与最佳做法相反?

  public class I {/ * snip * /} 
public class D:字典< 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;私人集合}
/ * snip * /
}
public class C:Collection< I>
{
私人字典< 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); //还将项目添加到nameIndex
}
return item;
}
}

//// Snip:管理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集合类型通常不是一个好主意。主要原因是这些类型的大多数方法(如添加删除)不是虚拟的 - 如果您在派生类中提供自己的实现,如果您将集合作为基类型传递,则不会调用它们。在您的情况下,通过隐藏 Dictionary< TK,TV> indexer属性,您正在创建一种情况,其中使用基类引用的调用将执行与调用不同的调用使用派生类引用...违反 Liskov替代原则

  var derived = new D(); 
var firstItem = derived [puppy]; //添加小狗条目

var base =(Dictionary< string,I>)派生;
var secondItem = base [kitten]; //小猫没有添加.. BAD!

其次,更重要的是,创建一个插入一个项目的索引器您试图找到一个是完全意外的。索引器已经明确定义了 get 设置操作 - 实现 get 操作修改集合是非常糟糕的。



对于你所描述的情况,你最好创建一个可以对任何字典进行操作的扩展方法。这样的操作在它所做的操作上都不那么令人惊讶,也不需要创建派生的集合类型:

  public static类DictionaryExtensions 
{
public static TValue FindOrAdd< TKey,TValue>(
this IDictionary< TKey,TValue>字典,TKey键,TValue值)
其中TValue:new
{
电视价值;
if(!this.TryGetValue(key,out value))
{
value = new TValue();
this.Add(key,value);
}
返回值;
}
}


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;
        }
    }
}

Sample of how this may be used in a collection:

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)
}

解决方案

There's two problems that you should consider - both of which suggest this is a bad idea.

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!

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; 
    } 
}

这篇关于字典和收藏上的自动添加索引是否是一个很好的设计决策?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆