IEnumerable<T>
戻り型として使用することに問題はありますか? FxCop は返品について不平を言っていますList<T>
(代わりに返品を勧めていCollection<T>
ます)。
ええと、私は常に「できる限りのことは受け入れますが、最大限のものを返す」というルールに導かれてきました。
そういう意味では返すIEnumerable<T>
のは悪いことなのですが、「遅延検索」を使いたいときはどうすればいいのでしょうか?また、yield
キーワードはそのようなグッズです。
IEnumerable<T>
戻り型として使用することに問題はありますか? FxCop は返品について不平を言っていますList<T>
(代わりに返品を勧めていCollection<T>
ます)。
ええと、私は常に「できる限りのことは受け入れますが、最大限のものを返す」というルールに導かれてきました。
そういう意味では返すIEnumerable<T>
のは悪いことなのですが、「遅延検索」を使いたいときはどうすればいいのでしょうか?また、yield
キーワードはそのようなグッズです。
これは実際には2つの部分からなる質問です。
1)IEnumerable<T>を返すことに本質的に何か問題がありますか
何もありません。実際、C#イテレータを使用している場合、これは予想される動作です。それをList<T>または別のコレクションクラスに先制的に変換することは良い考えではありません。そうすることは、発信者による使用パターンを想定することです。発信者について何かを想定するのは良い考えではないと思います。IEnumerable<T>が必要な理由は十分にあるかもしれません。おそらく、完全に異なるコレクション階層に変換したいと考えています(この場合、リストへの変換は無駄になります)。
2)IEnumerable <T>以外のものを返すことが望ましい状況はありますか?
はい。発信者について多くのことを想定するのは良い考えではありませんが、自分の行動に基づいて決定を下すことはまったく問題ありません。絶えず更新されているオブジェクトにリクエストをキューに入れていたマルチスレッドオブジェクトがあるシナリオを想像してみてください。この場合、生のIEnumerable<T>を返すことは無責任です。コレクションが変更されるとすぐに、列挙可能なものが無効になり、実行が発生します。代わりに、構造のスナップショットを取り、その値を返すことができます。List<T>フォームで言います。この場合、オブジェクトを直接構造(またはインターフェイス)として返すだけです。
ただし、これは確かにまれなケースです。
いいえ、ここで返すIEnumerable<T>
のは良いことです。あなたが約束しているのは「(型指定された)値のシーケンス」だけだからです。LINQなどに最適で、問題なく使えます。
ToList
呼び出し元は、特に LINQ ( 、ToArray
など)を使用して、このデータをリスト (または何でも) に簡単に入れることができます。
このアプローチにより、すべてのデータをバッファリングするのではなく、値を遅延してスプールバックできます。間違いなく良いものです。先日も便利なIEnumerable<T>
裏ワザを書きました。
IEnumerable は私には問題ありませんが、いくつかの欠点があります。クライアントは、結果を取得するために列挙する必要があります。Count などをチェックする方法はありません。あまりにも多くのコントロールを公開しているため、リストは不適切です。クライアントはそれに追加/削除などを行うことができ、それは悪いことです。少なくとも FxCop の見解では、コレクションは最善の妥協案のようです。私は常に自分のコンテキストで適切と思われるものを使用します (たとえば、読み取り専用のコレクションを返したい場合は、コレクションを戻り値の型として公開し、List.AsReadOnly() を返すか、yield による遅延評価のために IEnumerable などを返します)。ケースバイケースで考えよう
あなたの原則について:「できる限り受け入れますが、最大限に返します」。
大規模なプログラムの複雑さを管理するための鍵は、情報隠蔽と呼ばれる手法です。メソッドが を構築するList<T>
ことによって機能する場合、その型を返すことによってこの事実を明らかにする必要はあまりありません。その場合、発信者は返されたリストを変更する可能性があります。これにより、キャッシングや遅延反復を行う機能が削除されますyield return
。
したがって、機能が従うべきより良い原則は、「あなたがどのように働いているかについてできるだけ明らかにしない」ということです。
本当に列挙のみを返す場合は、IEnumerable<T> を返すことは問題ありません。これは、呼び出し元によってそのまま消費されます。
しかし、他の人が指摘しているように、呼び出し元が他の情報 (カウントなど) を必要とする場合、列挙する必要があるという欠点があります。.NET 3.5 拡張メソッド IEnumerable<T>.Count は、戻り値が ICollection<T> を実装していない場合、バックグラウンドで列挙しますが、これは望ましくない可能性があります。
結果がコレクションの場合、IList<T> または ICollection<T> を返すことがよくあります。内部的にメソッドは List<T> を使用して、それをそのまま返すか、または保護したい場合は List<T>.AsReadOnly を返すことができます。変更に対して (たとえば、リストを内部的にキャッシュしている場合)。私の知る限り、FxCop はこれらのいずれにも非常に満足しています。
「できる限りのことは受け入れるが、最大限のものを返す」が私が提唱するものです。メソッドがオブジェクトを返すとき、実際の型を返さず、基本型を返すことによってオブジェクトの機能を制限する正当な理由は何ですか。ただし、これにより、インターフェイスを設計するときに「最大」(実際の型) がどうなるかをどのように知ることができるかという疑問が生じます。答えはとても簡単です。インターフェイス設計者が、アプリケーション/コンポーネントの外部で実装されるオープン インターフェイスを設計しているという極端な場合にのみ、実際の戻り値の型が何であるかを知りません。賢明な設計者は、メソッドが何をすべきか、最適な/ジェネリックな戻り値の型がどうあるべきかを常に考慮する必要があります。
たとえば、オブジェクトのベクトルを取得するためのインターフェイスを設計していて、返されるオブジェクトの数が可変になることがわかっている場合、賢明な開発者は常にリストを使用すると想定します。誰かが配列を返すことを計画している場合、彼/彼女が所有していない別のレイヤーからデータを返すだけでない限り、私は彼の能力に疑問を呈します。これがおそらく、FxCop が ICollection (List と Array の共通ベース) を提唱する理由です。
上記のように、他にも考慮すべき点がいくつかあります
返されたデータが可変または不変である必要がある場合
返されたデータが複数の呼び出し元で共有される場合
LINQ の遅延評価に関しては、95% 以上の C# ユーザーが不信感を理解していないと確信しています。それはとても非OOっぽいです。OO は、メソッド呼び出しで具体的な状態の変更を促進します。LINQ の遅延評価は、式の評価パターンでランタイム状態の変更を促進します (上級ユーザー以外が常に従うものではありません)。
重要な側面の1つは、を返すときにList<T>
実際に参照を返すことです。これにより、発信者がリストを操作できるようになります。これは一般的な問題です。たとえば、List<T>
GUIレイヤーに戻るビジネスレイヤーなどです。
IEnumerableを返すと言ったからといって、リストを返すことができないという意味ではありません。アイデアは、不要な結合を減らすことです。呼び出し元が気にする必要があるのは、リストを含めるために使用されるコレクションの正確なタイプではなく、リストを取得することだけです。配列に裏打ちされたものがある場合、Countのようなものを取得するのはとにかく速くなります。
あなた自身のガイダンスは素晴らしいと思います-パフォーマンスに影響を与えることなく何を返すかについてより具体的にすることができる場合(たとえば、結果からリストを作成する必要はありません)、そうしてください。ただし、関数がどのタイプを見つけるかを合法的に知らない場合、たとえば、ある状況ではリストを使用し、ある状況では配列を使用する場合など、IEnumerableを返すことが「最善」です。 。それを、あなたが返したいと思うかもしれないすべての「最大公約数」と考えてください。