私が取り組んでいる多くのプロジェクトでは、読み取り専用のコレクションを返す必要があるときはいつでも、IEnumerable<T>
インターフェイスを使用して、次のようにタイプ固有にします。
Public ReadOnly Property GetValues() As IEnumerable(Of Integer)
Get
'code to return the values'
End Get
End Property
ほとんどの場合、私はリストを返しますが、一部の関数と読み取り専用プロパティでは、拡張メソッドのおかげで目的を果たす配列を返します。
私の質問は特定のタイプの代わりにIEnumerable<T>
sを返すことによって設計原則に違反していますか(例:List<T>
、HashSet<T>
、Stack<T>
、またはArray
s)?
私は一般的にIEnumerable<T>
同様に。主なことは、実際の(最小限の)機能がメソッドから返される(またはメソッド引数の場合はメソッドに渡される)ことを自問することです。
結果セットを列挙するだけの場合は、IEnumerable<T>
まさにそれを行います。それ以上でもそれ以下でもありません。これにより、メソッドのフットプリントを壊すことなく、必要に応じて特定のケースでより具体的なタイプを返す柔軟性が得られます。
デビッドの答えはほとんどそれをカバーしています。 _IEnumerable<T>
_は一般的なベストプラクティスです。これは、ほとんどの新しいフレームワークメソッドを見るとわかります。
元の質問で読み取り専用コレクションを指定したので、それを返す前に_IEnumerable<T>
_インスタンスで.ToList().AsReadOnly()
を呼び出すことで、これを強制できます。これにより、_ReadOnlyCollection<T>
_の具体的なインスタンスが作成されて返されます。戻り値typeはまだ_IEnumerable<T>
_である必要があります。これは、呼び出し元が_ReadOnlyCollection<T>
_を具体的に返していることを知る必要がないためですが、このようにして、呼び出し元が射撃するのを防ぎます。自分の足で。
(この手順がないと、呼び出し元は、たとえば、メソッドから取得した_IEnumerable<T>
_を_List<T>
_にキャストしようとする可能性があります。キャストが成功した場合、それが最初に使用していたものである場合、呼び出し元は次のことができます。次に、メソッド内で操作しているのと同じリストを変更すると、予期しない結果が生じる可能性があります。)
個人的には、APIから_IEnumerable<T>
_を返すことは、実装が遅延評価を使用する可能性があることを強く示唆していると思います。
遅延評価を使用できることを知ることは、次のことを意味するため、呼び出し元にとって重要な場合があります。
結果を複数回繰り返すと(たとえば、カウントを取得してからデータにアクセスするため)、評価が複数回実行されます。
結果を反復処理しているときに、APIから例外をスローできます。
例えば.
_IEnumerable<MyObject> LazyEvaluatedApi()
{
}
...
IEnumerable<MyObject> result = LazyEvaluatedApi();
...
foreach(MyObject item in result) // Exception from LazyEvaluatedApi may be thrown here.
{
}
_
実装で遅延評価(データアクセス層APIなど)を使用する必要がないと思われる場合は、_ICollection<T>
_または_IList<T>
_を返すことをお勧めします。呼び出し元にCount
プロパティ(_ICollection<T>
_および_IList<T>
_)とインデクサー(_IList<T>
_のみ)へのアクセスを許可することとは別に、あなたはまた、遅延評価。
_ICollection<T>
_または_IList<T>
_を返す場合、具体的な実装は通常_List<T>
_を返します。リストが読み取り専用であることが重要な場合は、List<T>.AsReadOnly()
を返します。
それはあなたが何をしたいのか、そしてあなたが何を「違反」しようとしているのかによります。これは以下に依存します:
多くの優れた設計の達人と原則は、modizable-collection-readyよりもIEnumerable <>を返すことを好むことに注意してください。
IEnumerable<T>
を返すことは問題ありません。 Tが参照型の場合、呼び出し元がオブジェクトを変更する可能性があることに注意してください。
必要な最小限の機能を提供する最も一般的なタイプを返す必要があります。この場合、呼び出し元がデータを相互作用する必要があるだけであれば、IEnumerableはList<T>
、HashSet<T>
またはその他のコレクションタイプよりも適切です。このようにして、呼び出し元はメソッドの実装から切り離されたままになり、呼び出し元を中断することなく、将来的にメソッドの実装を自由に変更できます。
場合によります。メソッドの使用が単なる列挙である場合は、IEnumerableが正しい選択です。
インデックスルックアップなどの特定の機能が重要な場合は、より具体的にすることができます。
そうは言っても、いつでもIEnumerableをLINQ式でラップして、何でも使用できます。返された型がLINQ式で使用される機能をすでにサポートしている場合は、巧妙なコンパイラがその式を最適化できます。
IEnumerableは、クエリロジックを組み合わせることができるため、クエリが高速です。コレクションに影響を与えている間はコレクションを変更したくないので、通常は値の削除または変更に配列を使用します
List<T>
、HashSet<T>
などによって提供される追加機能が必要ない場合は、IEnumerable<T>
を返すことで問題ありません。
最も適切と思われるタイプを返します。コレクションをループするか、コレクションでLINQ-y処理を実行するだけでよい場合は、ほとんどの状況でIEnumerable<T>
が適しています。
「よりリッチな」型を返す必要がある場合は、具象型ではなくインターフェースを返すことを検討してください--IList<T>
ではなくList<T>
、ISet<T>
ではなくHashSet<T>
など-必要に応じて、呼び出しコードを壊すことなく、実装を将来変更できるようにします。