7

我的课程包含 a Dictionary<T, S> dict,我想公开 aReadOnlyCollection<T>的键。如何在不将 复制Dictionary<T, S>.KeyCollection dict.Keys到数组然后将数组公开为 的情况下做到这一点ReadOnlyCollection

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

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

4

6 回答 6

5

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

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

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

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

于 2008-11-12T14:39:03.457 回答
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
}

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

于 2008-11-12T16:25:21.603 回答
2

作为记录,在 .NET 4.6 中,KeyCollection<T>implements IReadOnlyCollection<T>,所以如果你使用那个接口,你仍然可以反映字典的变化,仍然得到 O(1) 包含,并且因为接口是协变的,你可以返回IReadOnlyCollection<some base type>

于 2016-06-01T15:07:37.303 回答
1

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

字典< T,S > d;

然后

ReadOnlyCollection< T > r = new ReadOnlyCollection< T >( d.Keys.ToList() );

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

于 2008-11-12T14:48:45.667 回答
1

不幸的是,据我所知,你不能直接做到这一点,因为KeyCollection<T>它不会暴露任何可以让你轻松做到这一点的东西。

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

于 2008-11-12T15:04:05.123 回答
0

这很丑陋,但这会做到这一点

Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));
于 2008-11-12T14:45:47.967 回答