【问题标题】:How to make such custom list/collection如何制作这样的自定义列表/集合
【发布时间】:2014-08-04 19:13:08
【问题描述】:

我正在尝试开发一个自定义集合或列表类,它为我提供以下功能:

Add(MyObject)
Add(MyObject, String)   ' key
Remove(MyObject)
RemoveByKey(String) ' key
RemoveAt(Index)
Count
Clear
Item(Index)
Item(String)    ' key
Contains(MyObject)
ContainsKey(String) ' key
GetEnumerator   ' for-each MyObject

我搜索了 IEnumerable、IList、ICollection,但没有一个能满足我上面的需求。例如,它们都缺少通过 Key(string) 存储对象。 如何创建这样的集合/列表对象?我注意到最符合我要求的是系统可用的 ListViewItemCollection 对象。我希望我能看到它里面的编码,以了解它是如何实现对象的存储和检索的。

有人可以帮忙吗?或者引导我访问教程链接。

谢谢。

【问题讨论】:

  • 听起来你想要一本字典。
  • 添加到@SLaks 评论,微软在此处提供Dictionary<TKey, TValue> 实现的源代码:referencesource.microsoft.com/#mscorlib/system/collections/…
  • 这看起来更像是OrderedDictionary,因为他也想通过索引访问项目。
  • 我会结合两个容器 - 字典用于那些有键和列表的所有容器。 Index和Key有关系吗?
  • @SLaks 我不建议使用OrderedDictionary,我将Dictionary 与他正在寻找的内容进行了比较。我不是要攻击您的评论,只是想提供帮助

标签: c# list collections ienumerable


【解决方案1】:

此类的示例可以是 System.Windows.Forms.Control.ControlCollection,它的实现方式类似于 List<KeyValuePair<string,Control>>(实际上 Control 已经包含密钥),this[string] 是使用普通 for 循环实现的(线性搜索密钥)。

我们可以通过添加字典来帮助加快速度,并将每个带有键的项目添加到两个集合(列表+字典)。没有键的项目仅添加到列表中。

编辑: 进一步改进可能会使用List<KeyValuePair<string,T>>Dictionary<string,KeyValuePair<int,T>> - 从列表映射索引到字典以便更快地删除。 RemoveAt 应检查密钥是否已预设并将其从字典中删除。 RemoveByKey 可以获取内部 List.RemoveAt 的索引。

基于 cmets 的 ADDON: IEnumerable<T> 的实现可能如下所示:

class MyObjectList<T>: IEnumerable<T> {
public IEnumerator<T> GetEnumerator() {
    T[] a = items; int n = size;
    for(int i = 0; i < n; i++)
        yield return a[i]; }
IEnumerator IEnumerable.GetEnumerator() {
    return GetEnumerator(); }

...以上是List&lt;T&gt;的内部结构

ADDON: here you can see my custom ListCoreList created from it(随意使用)。

【讨论】:

  • 速度不是问题。我想我正在尝试开发的收藏品不会超过 50 件。但是将对象保存在字典和列表中不是对内存的负担吗?
  • 好吧,如果速度不是问题,那么我会采用与 ControlCollection 相同的方式。但我喜欢优化速度,内存开销不会那么大(比如 *2 或 *3 - 这应该不是问题,速度可能)。
  • 那么让我直说吧。我使用内部列表和字典来管理 Add、Remove、RemoveAt、RemoveByKey 等。但是对于 GetEnumerator,我相信我需要实现一些接口。应该是 IList 或 IColelction 还是其他?
  • GetEnumerator 就足够了,但你应该添加IEnumerable&lt;T&gt; 接口。 GetEnumerator() 很简单:foreach(KeyValuePair&lt;string,T&gt; pair in innerList) yield return pair.Value;
  • 我检查了您的@firda ListCore 示例以及我在网上找到的一些其他自定义集合源代码,我注意到许多自定义集合正在使用 Array 来处理他们的工作,而不是依赖内置接口像 IList 或 ICollection。这些接口的性能真的很差,不得不求助于手动管理的数组吗?
【解决方案2】:

我敢打赌,有很多更简单的方法可以做到这一点,但这里有一种方法。您可以创建一个 struct 包含每个项目的键和值:

public sealed class Listionary<K, T> : IDictionary<K, T>, IList<T>
{
    private struct ListionaryPair
    {
        public ListionaryPair(T item) : this()
        {
            Item = item;
        }

        public ListionaryPair(K key, T item) : this()
        {
            Key = key;
            Item = item;
        }

        public K Key { get; private set; }
        public T Item { get; private set; }
        public bool HasKey { get; private set; }
    }

    private readonly List<ListionaryPair> list = new List<ListionaryPair>();

(整个HasKey 事物允许值类型为K,或null 引用作为有效键。如果你只想要string 键,你可以用KeyValuePair&lt;string, T&gt; 替换这个结构)

然后将两个接口分开:

    public void Add(T item)
    {
        list.Add(new ListionaryPair(item));
    }

    public void Add(K key, T item)
    {
        list.Add(new ListionaryPair(key, item));
    }

    public void RemoveAt(int index)
    {
        list.RemoveAt(index);
    }

您可以通过显式实现它们来隐藏丑陋的方法:

    void ICollection<KeyValuePair<K, T>>.CopyTo(KeyValuePair<K, T>[] array, int arrayIndex)
    {
        // code implementing the method
    }

您需要一些辅助方法来按键访问:

    private int IndexOfKey(K key)
    {
        for (int i = 0; i < list.Count; i++)
        {
            var pair = list[i];
            if (pair.HasKey && pair.Key == key)
            {
                return i;
            }
        }

        return -1;
    }

但如果你把它们做对了,剩下的就不会有太大的挑战了:

    public T this[K key]
    {
        get
        {
            int index = IndexOfKey(key);
            if (index < 0)
            {
                throw new IndexOutOfRangeException();
            }

            return list[index].Item;
        }
        set
        {
            int index = IndexOfKey(key);
            if (index < 0)
            {
                throw new IndexOutOfRangeException();
            }

            list[index] = new ListionaryPair(key, value);
        }
    }

完成每个接口方法都需要大量的编码,但大多数都会简短而简单。您必须决定是否允许多个项目具有相同的键,IDictionary&lt;,&gt;.Clear() 是清除整个集合还是仅清除键项目等。

此外,此示例中没有支持 Dictionary,因此性能可能不会那么好。

【讨论】:

  • public interface IDictionary&lt;TKey, TValue&gt; : ICollection&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;, IEnumerable&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;, IEnumerable ...所以,我宁愿坚持使用 KeyValuePair 并使用 null 表示无键。 ...但是 +1 的努力(很好和很长的答案)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多