【问题标题】:How to get a ReadOnlyCollection<T> of the Keys in a Dictionary<T, S>如何获取 Dictionary<T, S> 中键的 ReadOnlyCollection<T>
【发布时间】:2010-09-21 23:54:27
【问题描述】:

我的类包含一个Dictionary&lt;T, S&gt; dict,我想公开一个ReadOnlyCollection&lt;T&gt; 的键。如何在不将Dictionary&lt;T, S&gt;.KeyCollection dict.Keys 复制到数组然后将数组公开为ReadOnlyCollection 的情况下做到这一点?

我希望ReadOnlyCollection 成为一个合适的包装器,即。反映底层字典的变化,据我所知,将集合复制到数组不会这样做(而且看起来效率低下 - 我实际上并不想要一个新集合,只是为了公开底层的键集合.. .)。任何想法将不胜感激!

编辑:我使用的是 C# 2.0,因此没有可用的扩展方法,例如 .ToList(很容易)。

【问题讨论】:

  • 部分问题在于 ReadOnlyCollection 是 IList,而不仅仅是 ICollection。
  • 这太糟糕了,我讨厌 ReadOnlyCollection 不支持 IEnumerable 或至少 ICollection
  • @Shimmy: ReadOnlyCollection确实实现了 IEnumerable
  • 不,除了IList&lt;T&gt;,你不能初始化 ReadOnlyCollection
  • 真;我误解了你所说的“支持”是什么意思。构造函数采用 IList; ReadOnlyCollection 实现 IList、ICollection、IEnumerable 和接口的非泛型版本。

标签: c# generics dictionary collections c#-2.0


【解决方案1】:

如果你真的想使用ReadOnlyCollection,问题是ReadOnlyCollection的构造函数需要一个IList,而Dictionary的KeyCollection只是一个ICollection

因此,如果要将 KeyCollection 包装在 ReadOnlyCollection 中,则必须创建一个适配器(或包装器)类型,实现 IList,包装 KeyCollection。所以它看起来像:

var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);

虽然不是很优雅,尤其是 KeyCollection 已经是一个只读集合,你可以简单地将它作为 ICollection 传递:)

【讨论】:

  • 嗯。所以你说它是一个ICollection,它是只读的,但它不是一个ReadOnlyCollection。 :/多么不方便......
  • @JoelinGö:ReadOnlyCollection 的更好名称应该是 ReadOnlyListView&lt;T&gt;。如果它被赋予后一个名称,那么ICollection&lt;T&gt; 的只读实现不符合条件的原因就很明显了。
【解决方案2】:

这很丑,但是这样就可以了

Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));

【讨论】:

  • 这不会反映对基础字典的更改。
【解决方案3】:

假设您使用的是 C# 3.0 并且您有:

字典d;

然后

ReadOnlyCollection r = new ReadOnlyCollection( d.Keys.ToList() );

您还需要导入 System.Linq 命名空间。

【讨论】:

  • 不,这不会反映对未交付字典的更改;请参阅 jb evain 的回答。
  • 原发帖人说他们使用的是 C# 2.0,而不是 3.0。
【解决方案4】:

不幸的是,据我所知,KeyCollection&lt;T&gt; 没有公开任何可以让你轻松做到这一点的东西。

但是,您可以将 ReadOnlyCollection&lt;T&gt; 子类化,以便它的构造函数接收字典本身并覆盖适当的方法,以便它公开字典的项目,就好像它们是它自己的项目一样。

【讨论】:

    【解决方案5】:

    DrJokepu 说,为 Keys Collection 实现包装器可能很困难。但是,在这种特殊情况下,我认为实现并不是那么困难,因为正如我们所知,这是一个只读包装器。

    这让我们可以忽略一些在其他情况下难以实现的方法。

    这是 Dictionary.KeyCollection 包装器的快速实现:

    class MyListWrapper<T, TValue> : IList<T>
    {
        private Dictionary<T, TValue>.KeyCollection keys;
    
        public MyListWrapper(Dictionary<T, TValue>.KeyCollection keys)
        {
            this.keys = keys;
        }
    
        #region IList<T> Members
    
        public int IndexOf(T item)
        {
            if (item == null)
                throw new ArgumentNullException();
            IEnumerator<T> e = keys.GetEnumerator();
            int i = 0;
            while (e.MoveNext())
            {
                if (e.Current.Equals(item))
                    return i;
                i++;
            }
            throw new Exception("Item not found!");
        }
    
        public void Insert(int index, T item)
        {
            throw new NotImplementedException();
        }
    
        public void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }
    
        public T this[int index]
        {
            get
            {
                IEnumerator<T> e = keys.GetEnumerator();
                if (index < 0 || index > keys.Count)
                    throw new IndexOutOfRangeException();
                int i = 0;
                while (e.MoveNext() && i != index)
                {
                    i++;
                }
                return e.Current;
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    
        #endregion
    
        #region ICollection<T> Members
    
        public void Add(T item)
        {
            throw new NotImplementedException();
        }
    
        public void Clear()
        {
            throw new NotImplementedException();
        }
    
        public bool Contains(T item)
        {
            return keys.Contains(item);
        }
    
        public void CopyTo(T[] array, int arrayIndex)
        {
            keys.CopyTo(array, arrayIndex);
        }
    
        public int Count
        {
            get { return keys.Count; }
        }
    
        public bool IsReadOnly
        {
            get { return true; }
        }
    
        public bool Remove(T item)
        {
            throw new NotImplementedException();
        }
    
        #endregion
    
        #region IEnumerable<T> Members
    
        public IEnumerator<T> GetEnumerator()
        {
            return keys.GetEnumerator();
        }
    
        #endregion
    
        #region IEnumerable Members
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return keys.GetEnumerator();
        }
    
        #endregion
    }
    

    这可能不是这些方法的最佳实现 :) 但这只是为了证明可以这样做。

    【讨论】:

    • 您忘记在 IndexOf 和 Indexer 中的枚举器上调用 Dispose。此外,在 e.Current 上调用 Equals 可能会导致 NullReferenceException。微软也不希望在 IndexOf 中抛出异常,如果找不到则返回 -1。
    【解决方案6】:

    作为记录,在 .NET 4.6 中,KeyCollection&lt;T&gt; 实现了IReadOnlyCollection&lt;T&gt;,因此如果您使用该接口,您仍然可以反映对字典的更改,仍然得到 O(1) 包含,并且因为该接口是协变,可以返回IReadOnlyCollection&lt;some base type&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 2010-10-09
      • 2011-07-28
      相关资源
      最近更新 更多