21

情報のコレクションを維持するマルチスレッド環境にオブジェクトがあります。

public IList<string> Data 
{
    get 
    {
        return data;
    }
}

現在、共有違反からコレクションを保護するために でreturn data;ラップしています。ReaderWriterLockSlimただし、念のために言うと、呼び出し元のコードがコレクションに変更を加えることができず、既に存在するもののみを表示できるように、コレクションを読み取り専用として返したいと思います。これはまったく可能ですか?

4

7 に答える 7

33

基になるデータがリストとして保存されている場合は、List(T).AsReadOnlyメソッドを使用できます。
データを列挙できる場合は、Enumerable.ToListメソッドを使用してコレクションを List にキャストし、AsReadOnly を呼び出すことができます。

于 2008-09-10T23:47:57.783 に答える
19

あなたの唯一の意図が間違いを犯さないように呼び出しコードを取得することであり、すべてを読み取る必要があるときにコレクションを変更することである場合、必要なのは追加、削除などをサポートしないインターフェイスを返すことIEnumerable<string>です。コードを呼び出すにはキャストする必要がありますが、アクセスしているプロパティの内部構造を知らずにキャストすることはほとんどありません。

ただし、呼び出し元のコードが他のスレッドからの更新を監視しないようにすることが目的の場合は、必要に応じてディープ コピーまたはシャロー コピーを実行するために、前述のソリューションにフォールバックする必要があります。

于 2008-09-11T01:45:52.977 に答える
19

私はあなたの受け入れられた回答に投票し、それに同意しますが、考慮すべきことを教えてもらえますか?

コレクションを直接返さないでください。コレクションの目的を反映した正確な名前のビジネス ロジック クラスを作成します。

これの主な利点は、コレクションにコードを追加できないという事実にあります。そのため、オブジェクト モデルにネイティブの「コレクション」がある場合は常に、オブジェクト モデルにアクセスするためにプロジェクト全体にオブジェクト指向以外のサポート コードが分散されます。

たとえば、コレクションが請求書である場合、未払いの請求書を反復処理するコード内の場所がおそらく 3 つか 4 つあるでしょう。getUnpaidInvoices メソッドを使用できます。ただし、「payUnpaidInvoices(payer, account);」のようなメソッドを考え始めると、真の力が発揮されます。

オブジェクト モデルを作成する代わりにコレクションを渡すと、リファクタリングのクラス全体が思い浮かぶことはありません。

これにより、問題が特にうまくいくことにも注意してください。人々がコレクションを変更したくない場合は、コンテナーにミューテーターを含める必要はありません。後で実際に変更する必要がある場合が 1 つだけあると判断した場合は、それを行うための安全なメカニズムを作成できます。

ネイティブ コレクションを渡しているときに、その問題をどのように解決しますか?

また、ネイティブ コレクションは、追加のデータで強化することはできません。次に (Collection、Extra) を 1 つまたは 2 つ以上のメソッドに渡すことがわかったときに、これに気付くでしょう。これは、「Extra」がコレクションを含むオブジェクトに属していることを示しています。

于 2009-12-18T23:47:34.580 に答える
11

ここで概念を混乱させていると思います。

は、既存のコレクションの読み取り専用ラッパーを提供し、呼び出し元 (クラス B) がコレクションを変更できない (つまり、要素を追加または削除ReadOnlyCollectionできない) ことを認識して、ユーザー (クラス A) がコレクションへの参照を安全に渡すことができるようにします。コレクション。)

スレッドセーフの保証はまったくありません。

  • あなた (クラス A) が基になるコレクションを として渡した後もそのコレクションを変更し続けるとReadOnlyCollection、クラス B はこれらの変更を確認し、イテレータを無効にするなどして、通常、コレクションに関する通常の同時実行の問題が発生する可能性があります。
  • さらに、コレクション内の要素が変更可能な場合、ユーザー (クラス A)呼び出し元 (クラス B) の両方が、コレクション内のオブジェクトの変更可能な状態を変更できます。

実装は必要に応じて異なります: - 呼び出し元 (クラス B) がコレクションにさらに変更を加えることを気にしない場合は、コレクションを複製して配布し、気にするのをやめることができます。- コレクションに加えられた変更を確認するために呼び出し元 (クラス B) が確実に必要であり、これをスレッドセーフにしたい場合は、さらに問題が発生します。1 つの可能性は、ReadOnlyCollection の独自のスレッド セーフなバリアントを実装して、ロックされたアクセスを許可することですが、IEnumerable をサポートしたい場合、これは自明ではなく、パフォーマンスも悪くなります。コレクション。

于 2008-09-12T17:16:00.720 に答える
3

降伏キーワードを使用します。IEnumerableリストをループして、yeildで結果を返します。これにより、コンシューマーはコレクションを変更せずにforeachを使用できます。

次のようになります。

List<string> _Data;
public IEnumerable<string> Data
{
  get
  {
    foreach(string item in _Data)
    {
      return yield item;
    }
  }
}
于 2008-09-21T08:08:52.867 に答える
3

akuの答えは、リストを読み取り専用として保護するだけであることに注意してください。リスト内の要素は依然として非常に書き込み可能です。非アトミック要素を読み取り専用リストに入れる前に複製せずに保護する方法があるかどうかはわかりません。

于 2008-09-11T00:22:24.860 に答える
2

代わりに、コレクションのコピーを使用できます。

public IList<string> Data {
get {
    return new List<T>(data);
}}

そうすれば、更新されても問題ありません。

于 2008-09-21T07:28:07.507 に答える