48

コレクション プロパティを持つオブジェクトを作成するたびに、それを行うための最良の方法を行ったり来たりしますか?

  1. プライベート変数への参照を返すゲッターを持つパブリック プロパティ
  2. 明示的な get_ObjList および set_ObjList メソッドは、毎回新しいオブジェクトまたは複製されたオブジェクトを返し、作成します
  3. IEnumerator を返す明示的な get_ObjList と IEnumerator を取る set_ObjList

コレクションが配列 (つまり、objList.Clone()) とリストの場合、違いはありますか?

依存関係が作成されるため、実際のコレクションを参照として返すことが非常に悪い場合、なぜプロパティを参照として返すのでしょうか? 子オブジェクトを参照として公開するときはいつでも、子にプロパティ変更イベントがない限り、親が「知る」ことなく、その子の内部を変更できます。メモリリークのリスクはありますか?

そして、オプション 2 と 3 はシリアライゼーションを壊しませんか? これはキャッチ 22 ですか、それともコレクション プロパティがある場合はいつでもカスタム シリアル化を実装する必要がありますか?

汎用の ReadOnlyCollection は、一般的な使用に適した妥協案のようです。IList をラップし、アクセスを制限します。おそらく、これはメモリリークとシリアライゼーションに役立ちます。ただし、まだ列挙に関する懸念があります

多分それはただ依存しています。コレクションが変更されても構わない場合は、#1 に従ってプライベート変数を介してパブリック アクセサーとして公開するだけです。他のプログラムにコレクションを変更させたくない場合は、#2 および/または #3 を使用することをお勧めします。

問題に暗示されているのは、あるメソッドを別のメソッドよりも使用する必要がある理由と、セキュリティ、メモリ、シリアライゼーションなどへの影響は何ですか?

4

7 に答える 7

56

コレクションをどのように公開するかは、ユーザーがコレクションをどのように操作するかによって完全に異なります。

1)ユーザーがオブジェクトのコレクションに項目を追加および削除する場合は、単純な取得専用コレクション プロパティが最適です (元の質問のオプション #1):

private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
  get { return this.myCollection_; }
}

この戦略は、ユーザーがコントロールに表示する項目を追加および削除するItemsWindowsForms および WPF コントロールのコレクションに使用されます。ItemsControlこれらのコントロールは、実際のコレクションを公開し、コールバックまたはイベント リスナーを使用してアイテムを追跡します。

WPF は、いくつかの設定可能なコレクションも公開して、ユーザーがItemsSourceプロパティItemsControl(元の質問のオプション #3) など、自分が制御するアイテムのコレクションを表示できるようにします。ただし、これは一般的な使用例ではありません。


2)ユーザーがオブジェクトによって維持されるデータのみを読み取る場合は、Quibblesomeが提案したように、読み取り専用コレクションを使用できます。

private readonly List<T> myPrivateCollection_ = new ...;
private ReadOnlyCollection<T> myPrivateCollectionView_;
public ReadOnlyCollection<T> MyCollection {
  get {
    if( this.myPrivateCollectionView_ == null ) { /* lazily initialize view */ }
    return this.myPrivateCollectionView_;
  }
}

ReadOnlyCollection<T>は、基になるコレクションのライブ ビューを提供するため、ビューを作成する必要があるのは 1 回だけであることに注意してください。

内部コレクションが を実装していないIList<T>場合、またはより高度なユーザーへのアクセスを制限したい場合は、代わりに列挙子を介してコレクションへのアクセスをラップできます。

public IEnumerable<T> MyCollection {
  get {
    foreach( T item in this.myPrivateCollection_ )
      yield return item;
  }
}

このアプローチは実装が簡単で、内部コレクションを公開せずにすべてのメンバーへのアクセスも提供します。ただし、変更後にコレクションを列挙しようとすると、BCL コレクション クラスが例外をスローするため、コレクションが変更されないままである必要があります。基になるコレクションが変更される可能性がある場合は、コレクションを安全に列挙する軽量ラッパーを作成するか、コレクションのコピーを返すことができます。


3)最後に、高レベルのコレクションではなく配列を公開する必要がある場合は、配列のコピーを返して、ユーザーが配列を変更できないようにする必要があります (元の質問のオプション #2)。

private T[] myArray_;
public T[] GetMyArray( ) {
  T[] copy = new T[this.myArray_.Length];
  this.myArray_.CopyTo( copy, 0 );
  return copy;
  // Note: if you are using LINQ, calling the 'ToArray( )' 
  //  extension method will create a copy for you.
}

ユーザーがいつ変更したかがわからないため、プロパティを介して基になる配列を公開しないでください。配列を変更できるようにするには、対応するSetMyArray( T[] array )メソッドを追加するか、カスタム インデクサーを使用します。

public T this[int index] {
  get { return this.myArray_[index]; }
  set {
    // TODO: validate new value; raise change event; etc.
    this.myArray_[index] = value;
  }
}

(もちろん、カスタム インデクサーを実装することで、BCL クラスの作業を複製することになります :)

于 2008-09-01T20:46:35.923 に答える
3

私は通常、System.Collections.ObjectModel.ReadOnlyCollection を返す public getter を使用します。

public ReadOnlyCollection<SomeClass> Collection
{
    get
    {
         return new ReadOnlyCollection<SomeClass>(myList);
    }
}

コレクションを変更するためのオブジェクトのパブリック メソッド。

Clear();
Add(SomeClass class);

クラスが他の人がいじるためのリポジトリであると想定されている場合は、独自の API を作成する手間が省けるので、方法 1 に従ってプライベート変数を公開するだけですが、本番コードではそれを敬遠する傾向があります。

于 2008-08-29T19:07:44.893 に答える
1

newIReadOnlyList<T>IReadOnlyCollection<T>Interfaces を使用してコレクションを公開することをお勧めします (.NET 4.5 が必要です)。

例:

public class AddressBook
{
    private readonly List<Contact> contacts;

    public AddressBook()
    {
        this.contacts = new List<Contact>();
    }

    public IReadOnlyList<Contact> Contacts { get { return contacts; } }

    public void AddContact(Contact contact)
    {
        contacts.Add(contact);
    }

    public void RemoveContact(Contact contact)
    {
        contacts.Remove(contact);
    }
}

コレクションが外部から操作できないことを保証する必要がある場合はReadOnlyCollection<T>、新しい不変コレクションを検討してください。

インターフェイスIEnumerable<T>を使用してコレクションを公開することは避けてください。このインターフェイスは、複数の列挙が適切に機能することを保証するものではありません。IEnumerable がクエリを表す場合、すべての列挙でクエリが再度実行されます。IEnumerable のインスタンスを取得した開発者は、それがコレクションを表しているのかクエリを表しているのかわかりません。

このトピックの詳細については、このWiki ページを参照してください。

于 2015-10-02T19:57:45.717 に答える
1

ReadOnlyCollection には、元のコレクションが不適切な時期に変更されないことを消費者が確信できないという欠点があります。代わりに、 Immutable Collectionsを使用できます。変更を行う必要がある場合は、オリジナルを変更する代わりに、変更されたコピーが提供されます。実装方法は、可変コレクションのパフォーマンスと競合します。または、オリジナルを何度もコピーして、後で各コピーにさまざまな (互換性のない) 変更を加える必要がない場合は、さらに優れています。

于 2015-05-18T21:55:21.717 に答える
0

インスタンスでコレクションを公開するだけの場合は、getter/setter をプライベート メンバー変数に使用することが、私にとって最も賢明な解決策のように思えます (最初に提案されたオプション)。

于 2008-08-29T18:52:57.410 に答える
0

ReadOnlyCollection(T) の使用が妥協であると提案するのはなぜですか? 元のラップされた IList で行われた変更通知を取得する必要がある場合は、ReadOnlyObservableCollection(T)を使用してコレクションをラップすることもできます。これは、あなたのシナリオでは妥協が少ないでしょうか?

于 2009-08-05T20:31:31.887 に答える
0

私は Java 開発者ですが、これは c# でも同じだと思います。

プログラムの他の部分が親に気付かれずに変更できるため、プライベートコレクションプロパティを公開することはありません。そのため、getter メソッドではコレクションのオブジェクトを含む配列を返し、setter メソッドではclearAll()コレクションに対して a を呼び出し、次にaddAll()

于 2008-08-29T19:50:03.387 に答える