68

拡張メソッドToList()はを返しますList<TSource>。同じパターンに従って、をToDictionary()返しますDictionary<TKey, TSource>

IList<TSource>これらのメソッドが戻り値をそれぞれおよびとして入力しない理由に興味がありIDictionary<TKey, TSource>ます。ToLookup<TSource, TKey>実際の実装ではなくインターフェイスとして戻り値を入力するため、これはさらに奇妙に思えます。

dotPeekまたは他の逆コンパイラーを使用してこれらの拡張メソッドのソースを見ると、次の実装がわかります(ToList()短いため表示されています)。

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { 
   if (source == null) throw Error.ArgumentNull("source");
   return new List<TSource>(source); 
}

では、なぜこのメソッドは、インターフェイス自体ではなく、インターフェイスの特定の実装として戻り値を入力するのでしょうか。唯一の変更はリターンタイプです。

IEnumerable<>これらの2つの場合を除いて、拡張機能の署名は非常に一貫しているので、私は興味があります。いつもちょっと変だと思っていました。

さらに、物事をさらに混乱させるために、州のドキュメントは次のとおりです。ToLookup()

指定されたキーセレクター関数に従って、IEnumerableからルックアップを作成します。

ただし、戻りタイプはILookup<TKey, TElement>です。

Edulinqで、Jon Skeetは、リターンタイプがList<T>の代わりであると述べていますが、それIList<T>以上主題に触れていません。
広範囲にわたる検索では答えが得られなかったので、ここで私はあなたに尋ねます:

戻り値をインターフェイスとして入力しないことの背後にある設計上の決定はありますか、それとも単なる偶然ですか?

4

10 に答える 10

50

返品には、その一部ではないメソッドを簡単に使用できるList<T>という利点があります。でできることとできないことはたくさんあります。List<T>IList<T>List<T>IList<T>

対照的に、()がないLookup<TKey, TElement>使用可能なメソッドは1つしかないため、とにかくそれを使用することにはならないでしょう。ILookup<TKey, TElement>ApplyResultSelector

于 2013-02-07T14:23:37.707 に答える
12

これらの種類の決定は恣意的に感じるかもしれませんが、どちらも実装しているが、通常の型のオブジェクトには存在しない他のメンバーを追加するため、インターフェイスではなくToList()リターンになると思います。List<T>List<T>IList<T>IList<T>

たとえば、AddRange()

IList<T>を実装する必要があるかを確認してください(http://msdn.microsoft.com/en-us/library/5y536ey6.aspx):

public interface IList<T> : ICollection<T>, 
    IEnumerable<T>, IEnumerable

そしてList<T>http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx):

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable

独自のコードは、、またはを必要としないかもしれませんがIReadOnlyList<T>IReadOnlyCollection<T>.NET ICollectionFrameworkおよび他の製品の他のコンポーネントは、より特殊なリストオブジェクトに依存している可能性があるため、.NET開発チームはインターフェイスを返さないことにしました。

常にインターフェイスを返すことがベストプラクティスであるとは思わないでください。それはあなたのコードまたはサードパーティのものがそのようなカプセル化を必要とする場合です。

于 2013-02-07T14:29:35.860 に答える
8

Listを超えるだけで多くの利点がありIListます。そもそも、そうでないListメソッドがあります。IListまた、実装がどのようなものであるかを知っているので、それがどのように動作するかについて推論することができます。あなたはそれが効率的に最後に追加できることを知っていますが、最初ではなく、それがインデクサーが非常に速いことなどを知っています。

LinkedList構造がに変更され、アプリケーションのパフォーマンスが低下することを心配する必要はありません。このようなデータ構造に関しては、多くのコンテキストで、従う契約だけでなく、データ構造がどのように実装されているかを知ることが非常に重要です。それは決して変わらないはずの振る舞いです。

IListまた、を受け入れるメソッドにを渡すことはできませんList。これは、よく見られることです。 は、制御できない署名と一致させるために、のインスタンスが本当に必要であり、それを支援しないToListため、頻繁に使用されます。ListIList

次に、戻ることにはどのような利点があるのか​​を自問しIListます。まあ、私たちはおそらくリストの他の実装を返すことができますが、前に述べたように、これは非常に有害な結果、ほぼ確実に、他のタイプを使用することで得られる可能性よりもはるかに多くの結果が得られます。実装の代わりにインターフェースを使用することはあなたに暖かいあいまいさを与えるかもしれません、しかしそれでさえ私がこの文脈で(一般的にまたは)良い考え方であると私が感じないものです。原則として、インターフェイスを返すことは、具体的な実装を返すことよりも一般的には好ましくありません。「受け入れるものには寛大であり、提供するものには具体的である。」メソッドのパラメーターは、可能な場合、呼び出し元が必要なことを実行する実装で渡すことができる必要な機能の最小量を定義するインターフェースである必要があります。また、呼び出し元と同じように実装を具体的に提供する必要があります。そのオブジェクトが可能な限りの結果で彼らができるように見ることを「許可」されました。

では、「なぜ戻るILookupのではなく、なぜ戻るのLookupか」に移ります。さて、最初Lookupは公開クラスではありません。LookupにはありませんSystem.Collections.*。LINQを介して公開されるLookupクラスは、コンストラクターを公開しません。を介さない限り、クラスを使用することはできませんToLookup。また、によってまだ公開されていない機能も公開されていませんILookup。この特定のケースでは、彼らはこの正確なメソッド(ToLookup)を中心にインターフェースを特別に設計し、Lookupクラスはそのインターフェースを実装するために特別に設計されたクラスです。このすべてのために、議論された事実上すべてのポイントはListここでは当てはまりません。戻るのは問題だっただろうかLookup代わりに、いいえ、そうではありません。この場合、どちらの方法でもまったく問題にはなりません。

于 2013-02-07T15:14:28.267 に答える
6

私の意見では、aを返すList<T>ことは、メソッド名がと言っているという事実によって正当化されますToList。それ以外の場合は、という名前を付ける必要がありますToIList。このメソッドの目的は、非特定IEnumerable<T>のものを特定のタイプに変換することList<T>です。

のような不特定の名前のメソッドがある場合は、またはのようなGetResults戻り型が適切と思われます。IList<T>IEnumerable<T>


Lookup<TKey, TElement>リフレクターを使用したクラスの実装を見ると、internalLINQ自体にのみアクセスできる多くのメンバーが表示されます。パブリックコンストラクタはなく、Lookupオブジェクトは不変です。Lookupしたがって、直接公開してもメリットはありません。

Lookup<TKey, TElement>クラスは一種のLINQ内部のようであり、公的な使用を目的としたものではありません。

于 2013-02-07T14:31:38.990 に答える
4

IList<>の代わりにList<>を返すという決定は、ToListを呼び出すためのより一般的なユースケースの1つが、リスト全体の即時評価を強制することであると信じています。リスト<>を返すことにより、これが保証されます。IList <>を使用すると、実装が怠惰になる可能性があり、呼び出しの「主な」目的が無効になります。

于 2013-02-07T17:34:34.287 に答える
3

これは、プログラマーがインターフェースと具象型の使用に関して理解するのが難しい一般的なことの1つです。

List<T>実装する具象を返すことIList<T>は、メソッドの消費者により多くの情報を与えるだけです。Listオブジェクトが(MSDN経由で)実装するものは次のとおりです。

[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, 
    IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable

として戻ると、それ自体List<T>に加えて、これらすべてのインターフェースのメンバーを呼び出すことができますList<T>。たとえば、には存在しますが、には存在しないためList.BinarySearch(T)、でのみ使用できます。List<T>List<T>IList<T>

一般に、メソッドの柔軟性を最大化するには、最も抽象型をパラメーターとして(つまり、使用するもののみ)、可能な限り最小の抽象型を返す必要があります(より機能的な戻りオブジェクトを許可するため)。

于 2013-02-08T02:37:08.347 に答える
3

一般に、具体的な型を探しているメソッドでToList()を呼び出すと、アイテムはIEnumerable型のままになる可能性があります。具体的なリストが必要なことをしているのでない限り、リストに変換する必要はありません。

于 2013-02-07T14:17:45.320 に答える
2

List<T>実際には、IListだけでなく、さまざまなインターフェイスを実装しているためです。

public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable{

}

これらの各インターフェイスは、リストが準拠する必要のある機能の範囲を定義します。特定のものを1つ選択すると、実装の大部分が使用できなくなります。

IListを返したい場合は、独自の単純なラッパーを使用することを妨げるものはありません。

public static IList<TSource> ToIList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException(source);
    return source.ToList();
}
于 2013-02-07T20:18:18.997 に答える
2

簡単に言えば、一般的に、利用可能な最も具体的なタイプを返すことは、信頼できるフレームワーク設計ガイドラインによって推奨されているということです。(申し訳ありませんが、手元に引用はありませんが、反対を好むJavaコミュニティガイドラインとは対照的に、これが突き出ていたので、はっきりと覚えています)。

これは私には理にかなっています。たとえば、いつでも実行できIList<int> list = x.ToList()ます。具体的なリターンタイプをサポートできるかどうかを考慮する必要があるのは、ライブラリの作成者だけです。

ToLookup<T>群衆の中でユニークなものです。しかし、完全にガイドラインの範囲内です。これ、図書館の作者が喜んでサポートする利用可能な最も具体的なタイプです(他の人が指摘しているように、具体的なLookup<T>タイプは、公用ではない内部タイプのようです)。

于 2013-02-07T21:34:18.800 に答える
1

関数が新しく構築された不変オブジェクトを返す場合、呼び出し元は通常、関数に含まれる実際のデータを保持できるのであれば、返される正確な型を気にする必要はありません。たとえば、を返すことになっている関数は、IImmutableMatrix通常ImmutableArrayMatrix、非公開の配列に裏打ちされたものを返す可能性がありますが、すべてのセルがゼロを保持している場合は、代わりに、フィールドZeroMatrixのみに裏打ちされた、を返す可能性があります(単にすべてゼロを返すゲッターを使用)時間)。呼び出し元は、マトリックスが与えられたのか;が与えられたのかを気にしません。どちらのタイプでも、すべてのセルを読み取ることができ、値が変更されないことが保証されます。これが、呼び出し元が気にすることです。WidthHeightImmutableArrayMatrixZeroMatrix

一方、オープンエンドのミューテーションを可能にする新しく構築されたオブジェクトを返す関数は、通常、呼び出し元が期待する正確なタイプを返す必要があります。呼び出し元がさまざまな戻りタイプを要求できる手段がない限り(たとえば、呼び出しToyotaFactory.Build("Corolla")ToyotaFactory.Build("Prius"))、宣言された戻りタイプが他のものである理由はありません。不変のデータ保持オブジェクトを返すファクトリは、含まれるデータに基づいてタイプを選択できますが、自由に変更可能なタイプを返すファクトリには、どのデータが入れられるかを知る方法がありません。異なる呼び出し元が異なるニーズを持つ場合(たとえば、現存する例に戻ると、一部の呼び出し元のニーズは配列で満たされますが、他の呼び出し元は満たされません)、ファクトリメソッドの選択肢を提供する必要があります。

ところで、のようなものIEnumerator<T>.GetEnumerator()は少し特殊なケースです。返されるオブジェクトはほとんどの場合変更可能ですが、非常に制約された方法でのみです。実際、返されるオブジェクトは、そのタイプに関係なく、列挙シーケンス内の位置という1つの可変状態を持つことが期待されます。anIEnumerator<T>は変更可能であると予想されますが、派生クラスの実装で変化する状態の部分は変更可能ではありません。

于 2013-02-07T15:50:10.463 に答える