] [TからのIEnumerator を取得することが可能ですか?
-
21-09-2019 - |
質問
レッツは、私はスレッドセーフデフォルトであるコレクションクラスを作成したいとします。
は内部的には、クラスはList<T>
と呼ばれる保護Values
プロパティがあります。
まず第一に、それはクラスがICollection<T>
を実装持っている意味があります。このインタフェースのメンバーの中には、実装するのは非常に簡単です。例えば、Count
リターンのthis.Values.Count
。
しかしICollection<T>
を実装するには、スレッドセーフなコレクションのために少しトリッキーである、IEnumerable<T>
(非ジェネリック)と同様IEnumerable
を実装するために私を必要とします。
もちろん、私はいつもNotSupportedException
とIEnumerable<T>.GetEnumerator
にIEnumerable.GetEnumerator
を投げることができますが、私には警官アウトのようなと感じています。
私はすでにgetValues
にロックし、Values
配列の形式でコピーを返しスレッドセーフT[]
機能を持っています。次のコードは、実際にスレッドセーフになるように、私の考えはGetEnumerator
を返すことによってthis.getValues().GetEnumerator()
を実装したのでます:
ThreadSafeCollection coll = new ThreadSafeCollection ();
// add some items to coll
foreach (T value in coll) {
// do something with value
}
残念ながら、この実装は唯一のジェネリックバージョンではない、IEnumerable.GetEnumerator
のために動作しているようです(そのため、上記のコードはInvalidCastException
をスローします)。
仕事に見えた私が持っていた一つのアイデアは、それにT[]
を呼び出す前にgetValues
にIEnumerable<T>
からGetEnumerator
の戻り値をキャストすることでした。代替は、最初の場所でgetValues
を返すように変更IEnumerable<T>
になるが、その後非ジェネリックIEnumerable.GetEnumerator
のために、単に非ジェネリックgetValues
にIEnumerable
からの戻り値をキャストします。これらのアプローチは、ずさんなまたは完全に受け入れを感じる場合しかし、私は実際に決定することはできません。
いずれにせよ、誰もがこれをやって行く方法の良いアイデアを持っているのですか?私は.Synchronized
方法を聞いたことがあるが、彼らは唯一のSystem.Collections
名前空間の非ジェネリックコレクションのために利用可能であるように見えます。たぶん、すでに.NETで存在するこの一般的な変種は、私は単にについて知らないことがありますか?
解決
ほとんどのコレクションは、繰り返し処理中にコレクションから物事を追加または削除することができないことを指定します。スレッドセーフなコレクションのために、あなたがいずれかのスレッドが反復されている間、コレクションを変更するから、他のスレッドをロックアウトすることになります。これは、イテレータの構文を使用して行うのは簡単であるべきであり、コピーを作成する必要はありません。
public IEnumerator<T> GetEnumerator()
{
lock (this.Values) // or an internal mutex you use for synchronization
{
foreach (T val in this.Values)
{
yield return val;
}
}
yield break;
}
このコレクションを変更する可能性のある他のすべての操作もthis.Values
をロックすることを前提としています。限りそれは本当だとして、あなたは問題ないはずです。
他のヒント
残念ながら、この実装は唯一のジェネリックバージョンではない、IEnumerable.GetEnumeratorのために働くようだ(と上記のコードは、InvalidCastExceptionがスローされますので)。
私のために奇妙なようです。あなたが明示的に非ジェネリックのIEnumerableを実装していますか?即ちあなたが書かれている。
public IEnumerator<T> GetEnumerator() { ...}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator<T>(); }
また、あなたはイテレータの構文でのIEnumerableを実装しようとしています。それは簡単なはずます:
public IEnumerator<T> GetEnumerator()
{
T[] values;
lock(this.Values)
values = this.Values.ToArray();
foreach(var value in values)
yield return value;
}
あなたがしようとした場合、なぜそれが
?あなたのニーズに合っていませんは型T[]
は、それが由来する型System.Array
に特別realtionshipを有しています。ジェネリック医薬品は、(そうでない構文がされている可能性がT[]
).NETで導入された前Array<T>
のような配列が行われました。
型System.Array
一の GetEnumerator()
のインスタンスメソッドを有します、public
。この方法は、(.NET 1以降)非ジェネリックIEnumerator
を返します。そしてSystem.Array
はGetEnumerator()
のための明示的なインターフェイスの実装を持っていません。これはあなたの観察結果を説明します。
しかし、特別な「ハック」はT[]
はIList<T>
及び(IEnumerable<T>
一つとなっている)、そのベースインタフェースを実装有するように作製しました。
((IList<T>)(this.getValues())).GetEnumerator()
私はこれをチェックしています.NETのバージョンでは、次のクラスが存在します:
namespace System
{
public abstract class Array
{
private sealed class SZArrayEnumerator // instance of this is returned with standard GetEnumerator() on a T[]
{
}
}
internal sealed class SZArrayHelper
{
private sealed class SZGenericArrayEnumerator<T> // instance of this is returned with generic GetEnumerator() on a T[] which has been cast to IList<T>
{
}
}
}