2008-08-29 26 views
43

コレクションプロパティを持つオブジェクトを作成するたびに、私はそれを行う最善の方法を前後に行っていますか?コレクションプロパティを公開するにはどうすればよいですか?

    が戻り、新規またはクローン化された オブジェクトを毎回作成
  1. 明示get_ObjListとset_ObjList 方法プライベート変数への参照を返すゲッターと
  2. パブリックプロパティ のIEnumeratorを返し
  3. 明示get_ObjListと がIEnumeratorを取るset_ObjList

コレクションが配列(objList.Clone())とList?

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

オプション2と3でシリアル化を中断しないでください。これはキャッチ22か、コレクションプロパティを持つたびにカスタムシリアル化を実装する必要がありますか?

一般的なReadOnlyCollectionは、一般的な使用のための優れた妥協のようです。 IListをラップし、アクセスを制限します。たぶん、これはメモリリークとシリアライゼーションに役立ちます。しかしそれはまだありますenumeration concerns

多分それは依存します。コレクションが変更されても構わない場合は、#1のプライベート変数を介してpublicアクセサーとして公開してください。他のプログラムがコレクションを変更しないようにするには、#2または#3が良いでしょう。

なぜ暗黙のうちに、ある方法を別のものよりも使い、セキュリティ、メモリ、シリアライゼーションなどに影響を与えるのはなぜですか?

答えて

52

コレクションを公開する方法は、ユーザーがそのコレクションとやり取りする方法によって異なります。

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

この戦略:

1)ユーザーが追加し、オブジェクトのコレクションから項目を削除する場合は、単純なGET専用のコレクションプロパティは、元の質問から最高(オプション#1)でありますWindowsフォームおよびWPF ItemsControlコントロールのItemsコレクションに使用されます。このコントロールでは、ユーザーがコントロールを表示したいアイテムを追加および削除します。これらのコントロールは、実際のコレクションを公開し、コールバックまたはイベントリスナーを使用してアイテムを追跡します。

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


2)ユーザーは唯一のオブジェクトによって維持されるデータを読み込むことは、あなたは読み取り専用のコレクションを使用することができますされている場合、Quibblesomeが示唆したように:ReadOnlyCollection<T>がのライブビューを提供

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_; 
    } 
} 

に留意されたいです。基本的なコレクションなので、一度ビューを作成するだけで済みます。

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

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

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


3)使用すると、配列ではなく、より高いレベルのコレクションを公開する必要がある場合は最後に、あなたはorginal質問から(オプション#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のクラスの作品を複製しますが:)

+0

配列は非常にリソース集約的なオブジェクトのボクシングとボックス解除を引き起こすことに注意してください。 – Brettski

+8

配列が強く型付けされている場合(int []、long []など)、要素にアクセスするとボクシングが発生しません。これは、オブジェクト[]を使用して値タイプ(つまり、オブジェクト[] a = new {1,2,3})を格納している場合にのみ発生します。 –

+0

ああ、公正なポイント、あなたは私の提案を改善しました。良いですね! :) – Quibblesome

0

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

3

私は通常、このために行く、System.Collections.ObjectModel.ReadOnlyCollectionを返し公共ゲッター:

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

とオブジェクトのパブリックメソッドは、コレクションを変更します。

Clear(); 
Add(SomeClass class); 

クラスは、それはあなた自身のAPIを書いて保存して、その後、私はちょうど方法#1ごとにプライベート変数を公開して台無しに他の人々のためのリポジトリのはずが、私は離れているから敬遠する傾向がされた場合プロダクションコードで。

+1

これは、非常にリソース集約的なコレクションプロパティの各読み取りで新しいReadOnlyCollectionを作成します。 – Ivan

+0

Aye、Emperor XLIIは、彼が掲載した上記の例の前提を改善します。 – Quibblesome

+0

@Ivan実際には、このコンストラクタを呼び出すことはO(1)操作であるため、リソースを大量に消費することはありません。これは単なるラッパーです。 http://msdn.microsoft.com/en-us/library/ms132476(v=vs.110).aspx – ForceMagic

0

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

私はプライベートコレクションプロパティを公開していません。プログラムの他の部分が親の通知なしに変更できるので、getterメソッドではコレクションのオブジェクトで配列を返し、セッターメソッドではclearAll()コレクションを公開する方法addAll()

0

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

0

ReadOnlyCollectionには、不適切な時間に元のコレクションが変更されないことをコンシューマが確かめることができないという欠点があります。代わりにImmutable Collectionsを使用できます。変更を行う必要がある場合は、オリジナルを変更する代わりに、変更されたコピーが与えられます。実装方法は、変更可能なコレクションのパフォーマンスと競合します。または、元のコピーを複数回コピーしてから、後で各コピーに異なる(互換性のない)変更を加える必要がない場合は、さらに優れた方法です。

0

新しいIReadOnlyList<T>IReadOnlyCollection<T>インターフェイスを使用してコレクションを公開することをお勧めします(.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 pageで読むことができます。

関連する問題