1

デフォルトでスレッドセーフなコレクション クラスを作成するとします。

内部的に、クラスには という保護されたList<T>プロパティがありますValues

まず、クラスを実装することは理にかなっていますICollection<T>。このインターフェイスのメンバーの一部は、実装が非常に簡単です。たとえば、 をCount返しますthis.Values.Count

しかし、実装するには、(非ジェネリック)と同様ICollection<T>に実装する必要があり、これはスレッドセーフなコレクションには少し注意が必要です。IEnumerable<T>IEnumerable

もちろん、私はいつでもNotSupportedExceptiononIEnumerable<T>.GetEnumeratorとを投げることができましIEnumerable.GetEnumeratorたが、それは私にとって警官のように感じます.

getValuesロックして配列Valuesの形式でコピーを返すスレッドセーフ関数が既にあります。したがって、私の考えは、次のコードが実際にスレッドセーフになるように、返すことでT[]実装することでした:GetEnumeratorthis.getValues().GetEnumerator()

ThreadSafeCollection coll = new ThreadSafeCollection ();

// add some items to coll

foreach (T value in coll) {
    // do something with value
}

残念ながら、この実装IEnumerable.GetEnumeratorは汎用バージョンではなく でのみ機能するようです (したがって、上記のコードは をスローしますInvalidCastException)。

私が持っていた 1 つのアイデアは、それを呼び出す前にからのT[]戻り値をキャストすることでした。代わりに、最初に を返すように変更することもできますが、非ジェネリックの場合は、戻り値を非ジェネリックにキャストするだけです。しかし、これらのアプローチがずさんな感じなのか、それとも完全に受け入れられるのか、私には判断できません。getValuesIEnumerable<T>GetEnumeratorgetValuesIEnumerable<T>IEnumerable.GetEnumeratorgetValuesIEnumerable

いずれにせよ、これを行う方法についてより良い考えを持っている人はいますか? メソッドについて聞いたことがありますが、名前空間.Synchronized内の非ジェネリック コレクションでのみ使用できるようです。System.Collections.NET に既に存在する、私が知らない一般的なバリアントがあるのではないでしょうか?

4

3 に答える 3

2

ほとんどのコレクションでは、反復中にコレクションに何かを追加したりコレクションから削除したりできないことが指定されています。したがって、スレッド セーフなコレクションの場合は、スレッドが反復している間、他のスレッドがコレクションを変更できないようにロックアウトする必要があります。これはイテレータ構文で簡単に実行でき、コピーを作成する必要はありません。

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。それが真実である限り、あなたは大丈夫です。

于 2009-10-15T14:53:16.477 に答える
1

残念ながら、この実装は、汎用バージョンではなく、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;
}

試してみた場合、なぜそれがあなたのニーズに合わないのですか?

于 2009-10-15T14:45:01.140 に答える
0

型には、派生T[]元の型との特別な関係があります。System.Arrayのような配列T[]は、ジェネリックが .NET に導入される前に作成されました (そうでなければ、構文は であった可能性がありますArray<T>)。

この型System.Arrayには 1 つのGetEnumerator()インスタンス メソッドがありpublicます。このメソッドは非ジェネリックを返しますIEnumerator(.NET 1 以降)。AndSystem.Arrayには、 の明示的なインターフェイス実装はありませんGetEnumerator()。これはあなたの観察を説明しています。

しかし、.NET 2.0 でジェネリックが導入されたとき、T[]実装IList<T>とその基本インターフェイス (そのうちのIEnumerable<T>1 つ) を持つ特別な "ハック" が行われました。このため、使用するのは完全に合理的だと思います:

((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>
    {
    }
  }
}
于 2013-02-20T13:39:51.097 に答える