都在字典和集合自动添加索引一个好的设计决定吗? [英] Are auto-adding indexers on Dictionaries and Collections a good design decision?

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

问题描述

当是它一个索引,自动添加项目集合/字典可以接受吗?这是合理的,或者违背最佳做法?



 公共类I {/ *剪断* /} 
公共类D:字典<字符串,I>
{
公共我这个[字符串名称]
{
得到
{
我的项目; (!this.TryGetValue(姓名,出项))
如果
{
项=新我();
this.Add(名称,项目);
}
回报率的项目;
}
}
}如何,这可能是



样品集合中使用的:

 公共类I 
{
公共I(字符串名称){/ *喀嚓* /}
公共字符串名称{;私人集; }
/ * *剪断/
}
公共类C:收集< I>
{
私人字典<字符串,I> nameIndex =新词典<字符串,I>();

公共我这个[字符串名称]
{
得到
{
我的项目;
如果
{
项=新我(姓名)(nameIndex.TryGetValue(姓名,出项)!);
this.Add(项目); //还会将项目添加到nameIndex
}
回报率的项目;
}
}

////剪断:管理nameIndex
//保护覆盖无效ClearItems()
//保护覆盖无效InsertItem代码(INT指数,我的项目)
//保护覆盖无效的removeItem(INT指数)
//保护覆盖无效SetItem(INT指数,我的项目)
}


解决方案

还有,你应该考虑两个问题 - 它们都表明这是一个糟糕的主意。



首先,从.NET BCL集合类型的继承是不是一个好主意。造成这种情况的主要原因是,这些类型的大多数方法(如添加删除)不是虚拟的 - 如果您提供在派生类中自己的实现,他们将不会得到,如果你身边经过您的收藏为基础类型调用。在你的情况,通过隐藏词典< TK,TV> 索引器属性,你正在创建的情况下使用基类的引用会做一些比调用不同的呼叫使用派生类的参考...违反了里氏替换原则的:

  VAR衍生=新D(); 
VAR =与firstItem衍生[小狗]; //添加小狗进入

VAR基地=(词典<字符串,I>)而得;
VAR secondItem =基本[小猫]; //小猫不加.. BAD!



其次,更重要的是,创建插入,当一个项目的索引你试图找到一个是完全出乎意料。索引有明确规定 GET 设置行动 - 实施 GET 操作来修改该集合是非常糟糕的。



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

 公共静态类DictionaryExtensions 
{
公共静态TValue FindOrAdd< TKEY的,TValue>(
本的IDictionary< TKEY的,TValue>字典,TKEY的关键,TValue值)
式TValue:新的()
{
TValue值;
如果
{
值=新TValue()(this.TryGetValue(键,超时值)!);
this.Add(键,值);
}
返回值;
}
}


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天全站免登陆