我的班级包含Dictionary<T, S> dict
,我希望公开ReadOnlyCollection<T>
个密钥。如何在不将Dictionary<T, S>.KeyCollection dict.Keys
复制到数组然后将数组公开为ReadOnlyCollection
的情况下执行此操作?
我希望ReadOnlyCollection
成为一个合适的包装器,即。反映基础字典中的变化,据我所知,将集合复制到数组将不会这样做(以及看似效率低下 - 我实际上并不想要新的集合,只是为了公开基础的密钥集合.. )。任何想法将不胜感激!
编辑:我正在使用C#2.0,因此没有.ToList(轻松)可用的扩展方法。
答案 0 :(得分:5)
如果你真的想使用ReadOnlyCollection&lt; T&gt;,问题是ReadOnlyCollection&lt; T&gt;的构造函数了。采用IList&lt; T&gt;,而词典的KeyCollection只是ICollection&lt; T&gt;。
因此,如果要将KeyCollection包装在ReadOnlyCollection中,则必须创建一个适配器(或包装器)类型,实现IList&lt; T&gt;,包装KeyCollection。所以它看起来像:
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
虽然不是很优雅,特别是因为KeyCollection已经是一个只读集合,并且你可以简单地将其传递为ICollection&lt; T&gt; :)
答案 1 :(得分:5)
这使我们可以忽略一些在其他情况下很难实现的方法。
这是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
}
这可能不是这些方法的最佳实现:)但它只是为了证明可以这样做。
答案 2 :(得分:1)
假设您使用的是C#3.0并且您拥有:
词典&LT; T,S&gt; d;
然后
ReadOnlyCollection&LT; T> r = new ReadOnlyCollection&lt; T&gt;(d.Keys.ToList());
您还需要导入System.Linq命名空间。
答案 3 :(得分:1)
不幸的是,就我所知,KeyCollection<T>
没有公开任何可以让你轻松做到这一点的事情,你不能直截了当。
但是,您可以继承ReadOnlyCollection<T>
,以便其构造函数接收字典本身并覆盖相应的方法,以便它将Dictionary的项目公开为它们自己的项目。
答案 4 :(得分:1)
对于记录,在.NET 4.6中,KeyCollection<T>
实现IReadOnlyCollection<T>
,因此如果使用该接口,您仍然可以反映对字典的更改,仍然可以获得O(1)包含,以及因为界面是协变的,你可以返回IReadOnlyCollection<some base type>
答案 5 :(得分:0)
这很难看,但这样做会
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));