ReadOnlyCollection< T>を取得する方法ディクショナリのキーの< T、S>
-
08-07-2019 - |
質問
私のクラスには Dictionary< T、S>が含まれていますdict
、キーの ReadOnlyCollection< T>
を公開します。 Dictionary< T、S> .KeyCollection dict.Keys
を配列にコピーし、配列を ReadOnlyCollection
として公開せずにこれを行うにはどうすればよいですか?
ReadOnlyCollection
を適切なラッパー、つまり基になるディクショナリの変更を反映するため、コレクションを配列にコピーしてもこれは行われません(また、非効率的と思われます-キーの基になるコレクションを公開するためだけに、新しいコレクションは実際には必要ありません)。 。)。どんなアイデアでも大歓迎です!
編集:C#2.0を使用しているため、.ToListなどの拡張メソッドを(簡単に)利用できません。
解決
ReadOnlyCollection< T>を本当に使用したい場合、問題はReadOnlyCollection< T>のコンストラクタです。辞書のKeyCollectionはICollection< T>のみですが、IList< T>を使用します。
したがって、KeyCollectionをReadOnlyCollectionでラップする場合は、IList< T>を実装してKeyCollectionをラップするアダプター(またはラッパー)タイプを作成する必要があります。したがって、次のようになります。
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
ただし、特にKeyCollectionは既に読み取り専用のコレクションであり、ICollection&lt; T&gt;として簡単に渡すことができるため、あまりエレガントではありません。 :)
他のヒント
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
}
これはこれらのメソッドの最適な実装ではないかもしれません:)が、これが行われる可能性があることを証明するためだけのものです。
C#3.0を使用していて、次のものがあるとします:
辞書&lt; T、S&gt; d;
その後
ReadOnlyCollection&lt; T&gt; r =新しいReadOnlyCollection&lt; T&gt;(d.Keys.ToList());
System.Linq名前空間もインポートする必要があります。
残念ながら、 KeyCollection&lt; T&gt;
がこれを簡単に行えるようにするものを公開していない限り、あなたはそれを直接行うことはできません。
ただし、 ReadOnlyCollection&lt; T&gt;
をサブクラス化して、コンストラクターが辞書自体を受け取り、適切なメソッドをオーバーライドして、辞書の項目を独自の項目であるかのように公開することもできます。
.NET 4.6のレコードの場合、 KeyCollection&lt; T&gt;
は IReadOnlyCollection&lt; T&gt;
を実装するため、そのインターフェイスを使用すると、変更を引き続き反映できます。ディクショナリ、まだO(1)が含まれ、インターフェイスが共変であるため、 IReadOnlyCollection&lt; some base type&gt;
見苦しいですが、これでうまくいきます
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));