質問

レッツは、私はスレッドセーフデフォルトであるコレクションクラスを作成したいとします。

は内部的には、クラスはList<T>と呼ばれる保護Valuesプロパティがあります。

まず第一に、それはクラスがICollection<T>を実装持っている意味があります。このインタフェースのメンバーの中には、実装するのは非常に簡単です。例えば、Countリターンのthis.Values.Count

しかしICollection<T>を実装するには、スレッドセーフなコレクションのために少しトリッキーである、IEnumerable<T>(非ジェネリック)と同様IEnumerableを実装するために私を必要とします。

もちろん、私はいつもNotSupportedExceptionIEnumerable<T>.GetEnumeratorIEnumerable.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[]を呼び出す前にgetValuesIEnumerable<T>からGetEnumeratorの戻り値をキャストすることでした。代替は、最初の場所でgetValuesを返すように変更IEnumerable<T>になるが、その後非ジェネリックIEnumerable.GetEnumeratorのために、単に非ジェネリックgetValuesIEnumerableからの戻り値をキャストします。これらのアプローチは、ずさんなまたは完全に受け入れを感じる場合しかし、私は実際に決定することはできません。

いずれにせよ、誰もがこれをやって行く方法の良いアイデアを持っているのですか?私は.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.ArrayGetEnumerator()のための明示的なインターフェイスの実装を持っていません。これはあなたの観察結果を説明します。

ジェネリックは、.NET 2.0で導入されたときに、

しかし、特別な「ハック」は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>
    {
    }
  }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top