7

我有一个Dictionary<int, object> whereint是 的属性obj。有没有更好的数据结构呢?我觉得使用属性作为键是多余的。

Dictionary<int, obj>是容器类中的一个字段,它允许obj基于intid 号随机索引值。容器类中的简化(无异常处理)索引器如下所示:

obj this[int id]
{
     get{ return this.myDictionary[id];}
}

myDictionary前面提到的Dictionary<int, obj>持有物体的地方在哪里。

这可能是快速随机访问的典型方式,但我想获得第二意见。

4

4 回答 4

9

有一个KeyedCollection类。

编辑: KeyedCollection 可以在内部使用字典,但是对于这种特定场景,它的接口比原始字典更清晰,因为您可以直接按值查找。诚然,我觉得一般来说它不是很有用。

于 2010-01-28T12:41:21.630 回答
9

框架中没有执行此操作的具体类。不过有一个抽象的,KeyedCollection。您必须从该类派生您自己的类并实现 GetKeyForItem() 方法。这很简单,只需返回您要索引的属性的值。

这就是您需要做的所有事情,但请注意 ChangeItemKey()。当您用作键的属性更改值时,您必须做一些有意义的事情。如果您确保该属性是不可变的(只有一个吸气剂),那就很容易了。但是当你不这样做时会很尴尬,对象本身现在需要知道它存储在你的集合中。如果您不对其进行任何操作(调用 ChangeItemKey),则该对象会在集合中丢失,您将无法找回它。非常接近泄漏。

请注意 Dictionary<> 如何通过分别指定键值和对象来回避这个问题。您可能仍然无法找到该对象,但至少它不会因设计而丢失。

于 2010-01-28T13:32:54.690 回答
1

KeyedCollection如果出厂设置带来的额外开销不值得,您可以轻松实现自己的功能。原始KeyedCollectionin 在System.Collections.ObjectModel内部是 aDictionary<TKey, TItem>和 a List<TItem>,这意味着您可以在IList<>和上定义操作IDictionary<>。例如,您可以插入、按索引访问、按插入顺序遍历集合(所有这些都很IList<>方便),同时您可以基于键进行快速查找(借助字典)。这意味着当您添加或删除一个项目时,它们必须在两个基础集合上执行,除了用于保存额外的小内存开销List<>(但对象不会因此而复制)。虽然加法速度没有太大影响(List<>加法是 O(1)),移除速度受到一点影响。

如果您不关心插入顺序和按索引访问:

public class KeyedCollection<TKey, TItem> : ICollection<TItem>
{
    MemberInfo _keyInfo;
    Func<TItem, TKey> _keySelector;
    Dictionary<TKey, TItem> _dict;

    public TItem this[TKey key]
    {
        get { return _dict[key]; }
    }

    public int Count
    {
        get { return _dict.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public ICollection<TKey> Keys
    {
        get { return _dict.Keys; }
    }

    private ICollection<TItem> Items
    {
        get { return _dict.Values; }
    }

    public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
    {
        var keyExpression = keySelector.Body as MemberExpression;
        if (keyExpression != null)
            _keyInfo = keyExpression.Member;

        _keySelector = keySelector.Compile();
        _dict = new Dictionary<TKey, TItem>(comparer);
    }



    private TKey GetKeyForItem(TItem item)
    {
        return _keySelector(item);
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public bool Contains(TItem item)
    {
        return ContainsKey(GetKeyForItem(item));
    }

    public bool TryGetItem(TKey key, out TItem item)
    {
        return _dict.TryGetValue(key, out item);
    }

    public void Add(TItem item)
    {
        _dict.Add(GetKeyForItem(item), item);
    }

    public void AddOrUpdate(TItem item)
    {
        _dict[GetKeyForItem(item)] = item;
    }

    public bool UpdateKey(TKey oldKey, TKey newKey)
    {
        TItem oldItem;
        if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey))   // important
            return false;

        RemoveKey(oldKey);
        Add(oldItem);
        return true;
    }

    private bool SetItem(TItem item, TKey key)
    {
        var propertyInfo = _keyInfo as PropertyInfo;
        if (propertyInfo != null)
        {
            if (!propertyInfo.CanWrite)
                return false;

            propertyInfo.SetValue(item, key, null);
            return true;
        }

        var fieldInfo = _keyInfo as FieldInfo;
        if (fieldInfo != null)
        {
            if (fieldInfo.IsInitOnly)
                return false;

            fieldInfo.SetValue(item, key);
            return true;
        }

        return false;
    }

    public bool RemoveKey(TKey key)
    {
        return _dict.Remove(key);
    }

    public bool Remove(TItem item)
    {
        return RemoveKey(GetKeyForItem(item));
    }

    public void Clear()
    {
        _dict.Clear();
    }

    public void CopyTo(TItem[] array, int arrayIndex)
    {
        Items.CopyTo(array, arrayIndex);
    }

    public IEnumerator<TItem> GetEnumerator()
    {
        return Items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

我已经实施ICollection<TItem>以使其更符合标准 - 而且您还可以获得漂亮的集合初始化器语法!:)

示例用法:

var p1 = new Person { Name = "a" };
var p2 = new Person { Name = "b" };

var people = new KeyedCollection<string, Person>(p => p.Name) { p1, p2 };
// p1 == people["a"];
// p2 == people["b"];
于 2013-03-30T00:40:33.220 回答
0

C# 动态属性帖子似乎表明使用字典是一种流行的选择。其他帖子建议使用HashTable

字典与哈希表

于 2010-01-28T12:56:34.903 回答