コレクション プロパティを公開するにはどうすればよいですか?[閉まっている]
-
09-06-2019 - |
質問
コレクション プロパティを持つオブジェクトを作成するたびに、最適な方法を行ったり来たりすることになります。
- プライベート変数への参照を返すゲッターを持つ公有財産
- 明示的なget_objlistとset_objlistメソッドを毎回返して作成するメソッド
- IENumeratorとIENumeratorを取得するset_objlistを返す明示的なget_objlist
コレクションが配列 (つまり、objList.Clone()) である場合とリストである場合に違いはありますか?
実際のコレクションを参照として返すことが依存関係を作成するため非常に悪い場合、なぜプロパティを参照として返すのでしょうか?子オブジェクトを参照として公開すると、子にプロパティ変更イベントがない限り、親が「知らないうちに」その子の内部を変更できます。メモリリークのリスクはありますか?
また、オプション 2 と 3 はシリアル化を中断しませんか?これはキャッチ 22 ですか、それともコレクション プロパティがある場合は必ずカスタム シリアル化を実装する必要がありますか?
汎用の ReadOnlyCollection は、一般的な使用にとっては良い妥協点のように思えます。IList をラップし、IList へのアクセスを制限します。おそらくこれはメモリリークとシリアル化に役立つでしょう。しかし、それはまだあります 列挙に関する懸念
たぶんそれはただ状況に応じてです。コレクションが変更されても構わない場合は、#1 に従ってプライベート変数に対するパブリック アクセサーとして公開するだけです。他のプログラムがコレクションを変更したくない場合は、#2 や #3 の方が適しています。
質問の中に含まれているのは、なぜある方法を別の方法よりも使用する必要があるのか、セキュリティ、メモリ、シリアル化などにどのような影響があるのかということです。
解決
コレクションを公開する方法は、ユーザーがコレクションをどのように操作することを意図しているかによって決まります。
1) ユーザーがオブジェクトのコレクションに対して項目を追加したり削除したりする場合は、単純な取得専用のコレクション プロパティが最適です (元の質問のオプション #1)。
private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
get { return this.myCollection_; }
}
この戦略は次の目的で使用されます。 Items
WindowsForms および WPF のコレクション ItemsControl
コントロール。ユーザーはコントロールに表示したい項目を追加および削除します。これらのコントロールは実際のコレクションを公開し、コールバックまたはイベント リスナーを使用して項目を追跡します。
WPF は、ユーザーが制御する項目のコレクションを表示できるようにするために、いくつかの設定可能なコレクションも公開します。 ItemsSource
のプロパティ ItemsControl
(元の質問のオプション #3)。ただし、これは一般的な使用例ではありません。
2) ユーザーがオブジェクトによって維持されるデータの読み取りのみを行う場合は、次のように読み取り専用コレクションを使用できます。 屁理屈 提案しました:
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 クラスの作業が複製されることになります :)
他のヒント
私は通常、System.Collections.ObjectModel.ReadOnlyCollection を返すパブリック ゲッターを使用します。
public ReadOnlyCollection<SomeClass> Collection
{
get
{
return new ReadOnlyCollection<SomeClass>(myList);
}
}
そして、コレクションを変更するオブジェクトのパブリック メソッド。
Clear();
Add(SomeClass class);
クラスが他の人がいじるためのリポジトリであると想定されている場合は、独自の API を作成する手間が省けるため、メソッド #1 に従ってプライベート変数を公開するだけですが、実稼働コードではそれを避ける傾向があります。
単にインスタンスでコレクションを公開したい場合は、プライベート メンバー変数にゲッター/セッターを使用することが私にとって最も賢明な解決策のように思えます (最初に提案されたオプション)。
ReadOnlyCollection(T) の使用が妥協だと提案するのはなぜですか?元のラップされた IList に対して行われた変更通知をまだ取得する必要がある場合は、 ReadOnlyObservableCollection(T) コレクションを包みます。これはあなたのシナリオにおいて妥協の余地が少ないでしょうか?
私は Java 開発者ですが、これは C# でも同じだと思います。
プログラムの他の部分が親に気づかれずにプライベート コレクション プロパティを変更する可能性があるため、プライベート コレクション プロパティを公開することはありません。そのため、ゲッター メソッドではコレクションのオブジェクトを含む配列を返し、セッター メソッドでは clearAll()
コレクション全体を調べてから、 addAll()
ReadOnlyCollection には、元のコレクションが不適切なタイミングで変更されないことをコンシューマーが確信できないという欠点がまだあります。代わりに使用できます 不変コレクション. 。変更が必要な場合は、オリジナルを変更する代わりに、変更されたコピーが提供されます。実装方法では、可変コレクションのパフォーマンスと競合します。あるいは、元のファイルを何度もコピーして、後で各コピーにさまざまな (互換性のない) 変更を加える必要がなければ、さらに良いでしょう。
新しいものを使用することをお勧めします 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>
または新しい Immutable コレクション。
避ける インターフェースを使用して IEnumerable<T>
コレクションを公開します。このインターフェイスは、複数の列挙が適切に実行されるという保証を定義しません。IEnumerable がクエリを表す場合、すべての列挙でクエリが再度実行されます。IEnumerable のインスタンスを取得した開発者は、それがコレクションを表しているのかクエリを表しているのかわかりません。
このトピックの詳細については、こちらをご覧ください ウィキページ.