戻り値の型として IEnumerable<T>
-
22-08-2019 - |
質問
使用に問題はありますか IEnumerable<T>
戻り値の型として?FxCopは復帰について苦情を言う List<T>
(戻ることをお勧めします Collection<T>
その代わり)。
そうですね、私は常に「できる限り最小限のものを受け入れ、最大限のものを返す」というルールに従ってきました。
この観点から戻りますと、 IEnumerable<T>
は悪いことですが、「遅延取得」を使いたい場合はどうすればよいでしょうか?また、 yield
キーワードはとても良いものです。
解決
これは実際には2つの部分の質問です。
1)のIEnumerable
まったく何もありません。あなたがC#のイテレータを使用している場合、実際には、これは正常な動作です。前emptivelyリスト
2)IEnumerableを以外の何かを返すことが好ましいかもしれない何らかの事情がある
はい。それはあなたの発信者について多くを前提とする素晴らしいアイデアではありませんが、それはあなた自身の行動に基づいた意思決定を行うために完全に大丈夫です。あなたは常に更新されていたオブジェクトに要求をキューイングされたマルチスレッドのオブジェクトを持っていたシナリオを想像してみてください。生のIEnumerable
このはいえ、確かにまれなケースです。
他のヒント
いいえ、IEnumerable<T>
は、ここに戻るには、の良いのものです。 LINQなどのための理想的な、そして完全には使用ます。
呼び出し側は簡単にリストにこのデータを置く(または何でも)することができます - 特にLINQ(ToList
、ToArray
、など)と
このアプローチではなく、すべてのデータをバッファリングすることよりも、あなたが遅延した値をバックスプールすることができます。確かにグッディ。私が書いたアップ別の有用なIEnumerable<T>
トリックをする他の日、あまりにます。
あなたの原則について:「あなたができる最低を受け入れるが、最大を返す」
大規模なプログラムの複雑さを管理するための鍵は、の隠れの情報と呼ばれる技術です。あなたの方法はList<T>
を構築することで動作する場合、それはその型を返すことによって、この事実を明らかにすることが必要ではありません。そうした場合、その後、あなたの発信者は、彼らが戻って得るリストを変更することがあります。これはあなたのキャッシュを行う能力、またはyield return
と怠惰な反復を削除します。
だから、より良い原理はフォローする機能があるためです。「あなたはどのように動作するかについてできるだけ少しを明らかにする」
IEnumerableをは私が細かいですが、それはいくつかの欠点があります。クライアントが結果を得るために列挙しています。これは、カウントなどを確認する方法はありません あなたはあまりにも多くのコントロールを公開するので、リストは悪いです。クライアントは、それから/削除などを追加することができ、それは悪いことすることができます。 コレクションは、少なくともFxCopの見解では、最高のcompromomiseです。 私はオールウェイズ(私は、読み取り専用のコレクションを返したい場合、私は戻り値の型としてコレクションを公開し、(List.AsReadOnlyを返す。例えば)や利回りなどによる遅延評価のためのIEnumerable)私のコンテキストでappropiate思われるものを使用します。ケースバイケースでそれを取る。
「最低限のものは受け入れて、最大限のものを返す」というのが私の主張です。メソッドがオブジェクトを返す場合、実際の型を返さず、基本型を返すことでオブジェクトの機能を制限する必要がある理由は何ですか。ただし、インターフェイスを設計するときに「最大」(実際の型) が何になるかをどうやって知ることができるのかという疑問が生じます。答えはとても簡単です。インターフェイス設計者がアプリケーション/コンポーネントの外部で実装されるオープン インターフェイスを設計している極端な場合にのみ、実際の戻り値の型が何であるかがわかりません。賢明な設計者は、メソッドが何を行うべきか、最適/汎用の戻り値の型はどうあるべきかを常に考慮する必要があります。
例えば。オブジェクトのベクトルを取得するインターフェイスを設計していて、返されるオブジェクトの数が可変であることがわかっている場合、賢明な開発者は常にリストを使用すると想定します。誰かが配列を返そうと計画している場合、その人が所有していない別の層からデータを返すだけでない限り、私はその人の能力を疑問に思うでしょう。そしておそらくこれが、FxCop が ICollection (List と Array の共通ベース) を提唱する理由です。
上記のように、他にも考慮すべきことがいくつかあります
返されるデータが変更可能である必要があるか、不変である必要があるか
返されたデータが複数の呼び出し元で共有される場合
LINQ の遅延評価に関しては、95% 以上の C# ユーザーがインテスタシーを理解していないと思います。それはとても非ooっぽいです。OO は、メソッド呼び出し時の具体的な状態変更を促進します。LINQ 遅延評価は、式評価パターンでのランタイム状態の変更を促進します (上級ユーザー以外のユーザーが常に従うものではありません)。
戻るのIEnumerable
しかし、他の人が指摘するように、それは彼が(例えば、カウント用)他の情報を必要とする場合、呼び出し側が列挙する必要があるかもしれないという欠点があります。戻り値は望ましくないとすることができる、ICollectionを
私は、多くの場合、結果が収集されたときに
重要な側面の 1 つは、 List<T>
実際に返品しているのは 参照. 。これにより、発信者がリストを操作することが可能になります。これは一般的な問題です。たとえば、ビジネス レイヤーが List<T>
GUI レイヤーに。
あなたがIEnumerableを返すをしていると言う理由だけで、あなたがリストを返すことができないという意味ではありません。アイデアは不要な結合を低減することです。呼び出し側が気にすべきなのは、むしろ、そのリストを含むために使用されるコレクションの正確なタイプよりも、物事のリストを取得しています。あなたは配列に裏打ちされた何かを持っている場合は、カウントのようなものを得ることは、とにかく速いことになるだろう。
私は自分自身の指導は素晴らしいだと思う - あなたはパフォーマンスヒットせずに戻っているのかについて、より具体的にすることができれば、そう(あなたが例えば、あなたの結果のうち、リストを構築する必要はありません)。あなたの関数は、合法的にそれを見つけるために、何が起こっているかのタイプを知らない場合でも、いくつかの状況であなたはなど、一覧にして配列を持ついくつかで作業することがあります場合のように、その後のIEnumerableを返すことはあなたが行うことができます「最高」です。あなたが返すようにしたいかもしれないすべての「最大公倍数」と考えてます。
私が選択した答えを受け入れることはできません。説明したシナリオを扱うが、リストまたは任意の他、あなたの使用は、それらの一つではありませんを使用する方法があります。瞬間IEnumerableをあなたが呼び出し側がforeachの操作を行う可能性があることを前提とする必要が返されます。具体的なタイプは、リストまたはスパゲッティであれば、その場合には、それは問題ではありません。実際には、単にインデックスは項目が削除されている場合は特に問題はあります。
任意の戻り値は、スナップショットです。それは、それがキャッシュされていた場合、それがキャッシュされたコピーのクローンである必要があり、その場合にはIEnumerableをの現在の内容であってもよく、その後降伏リターンを使用します(SQLクエリのresutsのような)よりダイナミックことになっている場合。しかし、コンテナが意のままに変異することが可能とカウントし、インデクサのようなメソッドを供給することは、マルチスレッド、世界の災害のためのレシピです。私もあなたのコードは、の制御にすることになっているコンテナに追加または削除呼び出すための呼び出し側の能力にもらっていない。
また、あなたは、実装に具体的なタイプのロックを返します。今日は、内部であなたは、リストを使用しても良いです。明日は多分あなたはマルチスレッドになって、スレッドセーフ容器や配列やキューまたは辞書の値コレクションまたはLINQクエリの出力を使用したいです。あなたは具体的な戻り値の型に自分自身をロックした場合、あなたは、コードの束を変更したり、返す前に変換を行うにはいずれかの必要があります。