List<BusinessObject> または BusinessObjectCollection?
-
09-06-2019 - |
質問
C# ジェネリックが登場する前は、誰もが IEnumerable を実装したコレクション ベースを作成して、ビジネス オブジェクトのコレクションをコーディングしていました。
IE:
public class CollectionBase : IEnumerable
そしてそこからビジネス オブジェクト コレクションを派生します。
public class BusinessObjectCollection : CollectionBase
汎用リスト クラスが登場しましたが、代わりにそれを使用する人はいるでしょうか?私は次の 2 つの手法を折衷して使用していることがわかりました。
public class BusinessObjectCollection : List<BusinessObject>
私がこれを行うのは、単にリストを渡すのではなく、名前を厳密に型指定したいからです。
とは あなたの アプローチ?
解決
私は通常、何らかの理由でデータ構造をカプセル化し、その機能の限定されたサブセットを提供する必要がない限り、List を直接使用する派です。これは主に、カプセル化が特に必要ない場合、カプセル化を行うのは単なる時間の無駄だからです。
ただし、C# 3.0 の集約初期化機能では、カスタマイズされたコレクション クラスの使用を推奨する新しい状況がいくつかあります。
基本的に、C# 3.0 では、以下を実装するあらゆるクラスが許可されます。 IEnumerable
新しい集約初期化子構文を使用するための Add メソッドがあります。たとえば、Dictionary はメソッド Add(K key, V value) を定義しているため、次の構文を使用して辞書を初期化できます。
var d = new Dictionary<string, int>
{
{"hello", 0},
{"the answer to life the universe and everything is:", 42}
};
この機能の優れた点は、任意の数の引数を持つ add メソッドに対して機能することです。たとえば、次のコレクションがあるとします。
class c1 : IEnumerable
{
void Add(int x1, int x2, int x3)
{
//...
}
//...
}
次のように初期化することが可能です。
var x = new c1
{
{1,2,3},
{4,5,6}
}
これは、複雑なオブジェクトの静的テーブルを作成する必要がある場合に非常に役立ちます。たとえば、単に使用していた場合、 List<Customer>
顧客オブジェクトの静的リストを作成したい場合は、次のように作成する必要があります。
var x = new List<Customer>
{
new Customer("Scott Wisniewski", "555-555-5555", "Seattle", "WA"),
new Customer("John Doe", "555-555-1234", "Los Angeles", "CA"),
new Customer("Michael Scott", "555-555-8769", "Scranton PA"),
new Customer("Ali G", "", "Staines", "UK")
}
ただし、次のようなカスタマイズされたコレクションを使用する場合は、次のようになります。
class CustomerList : List<Customer>
{
public void Add(string name, string phoneNumber, string city, string stateOrCountry)
{
Add(new Customer(name, phoneNumber, city, stateOrCounter));
}
}
次に、次の構文を使用してコレクションを初期化できます。
var customers = new CustomerList
{
{"Scott Wisniewski", "555-555-5555", "Seattle", "WA"},
{"John Doe", "555-555-1234", "Los Angeles", "CA"},
{"Michael Scott", "555-555-8769", "Scranton PA"},
{"Ali G", "", "Staines", "UK"}
}
これには、要素ごとに要素タイプ名を再入力する必要がないため、入力しやすく読みやすいという利点があります。この利点は、要素タイプが長いか複雑な場合に特に大きくなります。
そうは言っても、これはアプリで定義されたデータの静的コレクションが必要な場合にのみ役立ちます。コンパイラなどの一部の種類のアプリでは、これらを常に使用します。一般的なデータベース アプリなど、すべてのデータをデータベースから読み込むため、そうでないものもあります。
私のアドバイスは、オブジェクトの静的コレクションを定義する必要がある場合、またはコレクション インターフェイスをカプセル化する必要がある場合は、カスタム コレクション クラスを作成することです。それ以外の場合は、単に使用します List<T>
直接。
他のヒント
その 推奨 パブリック API では List<T> を使用せず、Collection<T> を使用すること
ただし、それを継承している場合は、大丈夫です。
ただ使用することを好みます List<BusinessObject>
. 。型定義すると、コードに不要なボイラープレートが追加されるだけです。 List<BusinessObject>
特定のタイプです。ただのタイプではありません List
オブジェクトなので、依然として強く型付けされています。
さらに重要なのは、何かを宣言することです List<BusinessObject>
コードを読んでいる人全員が、どのような型を扱っているのかを簡単に判断できるようになります。コードが何であるかを理解するために検索する必要がなくなります。 BusinessObjectCollection
それは単なるリストであることを思い出してください。typedefing では、意味をなすために全員が従わなければならない一貫した (変更) 命名規則を要求する必要があります。
タイプを使用する List<BusinessObject>
ここでそれらのリストを宣言する必要があります。ただし、リストを返す場合 BusinessObject
, 返品を検討してください IEnumerable<T>
, IList<T>
または ReadOnlyCollection<T>
- つまりクライアントを満足させる可能な限り弱い契約を返します。
リストに「カスタム コードを追加」する場合は、リスト タイプの拡張メソッドをコード化します。繰り返しますが、これらのメソッドを可能な限り最も弱いコントラクトに付加します。
public static int SomeCount(this IEnumerable<BusinessObject> someList)
もちろん、拡張メソッドを使用して状態を追加することはできませんし、追加すべきではありません。そのため、新しいプロパティとその背後にフィールドを追加する必要がある場合は、サブクラス、またはそれを保存するラッパー クラスを使用してください。
私は2つの選択肢を行ったり来たりしてきました。
public class BusinessObjectCollection : List<BusinessObject> {}
または次のことを行うだけのメソッド:
public IEnumerable<BusinessObject> GetBusinessObjects();
最初のアプローチの利点は、メソッド シグネチャをいじらずに、基礎となるデータ ストアを変更できることです。残念ながら、以前の実装からメソッドを削除するコレクション型を継承した場合は、コード全体でそのような状況に対処する必要があります。
おそらく、その目的で独自のコレクションを作成することは避けるべきです。リファクタリング中または新しい機能の追加中に、データ構造のタイプを数回変更する必要があるのは非常に一般的です。あなたのアプローチでは、BusinessObjectList、BusinessObjectDictionary、BusinessObjectTree などに別のクラスを作成することになります。
クラス名が読みやすいという理由だけでこのクラスを作成する価値はあまりありません。そうですね、山かっこ構文はちょっと見苦しいですが、C++、C#、Java では標準なので、それを使用するコードを書かなくても、常にこの構文に遭遇することになります。
通常、私は「値を追加する」必要がある場合にのみ独自のコレクション クラスを派生します。たとえば、コレクション自体にタグ付けされた「メタデータ」プロパティが必要な場合などです。
私もジョナサンと全く同じことをしています...から継承するだけです List<T>
. 。両方の利点を最大限に活用できます。ただし、通常、追加する価値がある場合にのみこれを行います。 LoadAll()
方法でも何でも。
両方を使用できます。怠惰にとって、つまり生産性にとって、List は非常に便利なクラスであり、「包括的」でもあり、率直に言って YANGNI メンバーでいっぱいです。によって提案された賢明な議論/推奨事項と組み合わせると、 MSDN の記事 リストをパブリックメンバーとして公開することについてはすでにリンクされていますが、私は「3番目」の方法を好みます。
個人的には、デコレータ パターンを使用して、List から必要なものだけを公開します。
public OrderItemCollection : IEnumerable<OrderItem>
{
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
void Add(OrderItem item)
{
_orderItems.Add(item)
}
//implement only the list members, which are required from your domain.
//ie. sum items, calculate weight etc...
private IEnumerator<string> Enumerator() {
return _orderItems.GetEnumerator();
}
public IEnumerator<string> GetEnumerator() {
return Enumerator();
}
}
さらに、おそらく OrderItemCollection を IOrderItemCollection に抽象化して、将来的に IOrderItemCollection の実装を交換できるようにします (Collection などの別の内部列挙可能オブジェクトを使用することを好むかもしれません。パフォーマンスには、キーと値のペアのコレクションまたはセットを使用する可能性が高くなります) 。
私はほぼすべてのシナリオで汎用リストを使用します。派生コレクションの使用を検討するのは、コレクション固有のメンバーを追加する場合だけです。しかし、LINQ の登場により、その必要性さえ減少しました。
6/1、もう 6 個
どちらにしても同じことです。これを行うのは、カスタム コードを BusinessObjectCollection に追加する理由がある場合のみです。
ロードメソッドがリストを返すようにしないと、共通のジェネリッククラスにさらに多くのコードを記述して、それを機能させることができます。Load メソッドなど。
他の人が指摘したように、List を公に公開しないことをお勧めします。公開すると FxCop が泣き言を言うでしょう。これには、次のように List からの継承が含まれます。
public MyTypeCollection : List<MyType>
ほとんどの場合、パブリック API は必要に応じて IList (または ICollection または IEnumerable) を公開します。
独自のカスタム コレクションが必要な場合は、List ではなく Collection を継承することで FxCop を静かに保つことができます。
独自のコレクション クラスを作成することを選択した場合は、次の型をチェックアウトする必要があります。 System.Collections.ObjectModel 名前空間.
名前空間は、実装者がカスタム コレクションを簡単に作成できるようにするための基本クラスを定義します。
実際のリストへのアクセスをシールドしたい場合は、自分のコレクションを使用することが多いです。ビジネス オブジェクトを作成している場合、オブジェクトが追加/削除されているかどうかを知るためのフックが必要になる可能性があります。そのような意味では、BOCollection の方が良いアイデアだと思います。もちろん、それが必要ない場合は、List の方が軽量です。また、何らかのプロキシ処理が必要な場合は、IList を使用して追加の抽象化インターフェイスを提供することを確認することもできます (例:偽のコレクションはデータベースからの遅延ロードをトリガーします)
しかし...Castle ActiveRecord やその他の成熟した ORM フレームワークを検討してみてはいかがでしょうか?:)
ほとんどの場合、私は単純に List 方式を使用します。90% の確率で必要な機能がすべて提供され、何か「追加」が必要な場合は、それを継承して追加部分をコード化します。 。
私だったらこうします:
using BusinessObjectCollection = List<BusinessObject>;
これは、完全に新しいタイプではなく、エイリアスを作成するだけです。List<BusinessObject> を直接使用するよりも、それを使用するコードを変更せずに、将来のある時点でコレクションの基礎となる構造を自由に変更できるため (同じプロパティとメソッドを提供する限り)、この方法を好みます。
これを試してみてください:
System.Collections.ObjectModel.Collection<BusinessObject>
CollectionBase のような基本的なメソッドを実装する必要がなくなります。
これが方法です:
配列を返し、受け入れる IEnumerable<T>
=)