web-dev-qa-db-ja.com

IEnumerable vs List - 何を使うのですか?どうやって動くの?

私はEnumeratorがどのように機能するのか、そしてLINQについて疑問を抱いています。次の2つの単純選択を考えてください。

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

または

IEnumerable<Animal> sel = (from animal in Animals 
                           join race in Species
                           on animal.SpeciesKey equals race.SpeciesKey
                           select animal).Distinct();

これがより一般的な例のように見えるように、私は元のオブジェクトの名前を変更しました。クエリ自体はそれほど重要ではありません。私が聞きたいのはこれです:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. 私がIEnumerableを使用する場合、私が "sel"をデバッグして調べるとき、それがIEnumerableであることに気づいた、それはいくつかの興味深いメンバーを持っています: "inner"、 "outer"、 "innerKeySelector"そして "outerKeySelector"代議員のようです。 "inner"メンバーには "Animal"インスタンスが含まれていませんが、 "Species"インスタンスが含まれています。これは私にとって非常に奇妙なことです。 「外側」メンバーは「動物」インスタンスを含みません。私は2人の代表がどちらが入って、何がそこから出るのかを決定すると思いますか?

  2. "Distinct"を使用すると、 "inner"には6つの項目が含まれています(2つだけが異なるため、これは正しくありません)が、 "outer"には正しい値が含まれています。繰り返しになりますが、おそらく委任されたメソッドがこれを決定しますが、これはIEnumerableについて私が知っている以上のものです。

  3. 最も重要なことに、2つのオプションのうちどちらがパフォーマンス的に最も良いのでしょうか。

.ToList()による邪悪なリストへの変換?

それとも、列挙子を直接使用しますか?

可能であれば、このIEnumerableの使用方法を説明するリンクや、いくつかのリンクを追加してください。

589
Axonn

IEnumerableは動作を記述しますが、Listはその動作の実装です。 IEnumerableを使用すると、コンパイラは後まで作業を延期する機会を与えられ、おそらく途中で最適化されます。 ToList()を使用すると、コンパイラは結果をすぐに具体化するように強制されます。

LINQ式を「積み重ねる」ときはいつでもIEnumerableを使います。なぜなら、振る舞いを指定するだけでLINQに評価を遅らせ、場合によってはプログラムを最適化する機会を与えるからです。あなたがそれを列挙するまでLINQがどのようにデータベースを問い合わせるためにSQLを生成しないか覚えていますか?このことを考慮:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

これで、最初のサンプル( "AllSpotted")といくつかのフィルターを選択するメソッドができました。だから今あなたはこれを行うことができます:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

それで、ListをIEnumerableの上で使うほうが速いですか?クエリが複数回実行されるのを防ぎたい場合に限ります。しかし、それは全体的に優れていますか?上の例では、LeopardsとHyenasはそれぞれ単一のSQLクエリに変換され、データベースは関連する行のみを返します。しかし、AllSpotted()からListを返した場合、データベースが実際に必要なデータよりはるかに多くのデータを返す可能性があるため、実行が遅くなる可能性があり、クライアントでフィルタリングを実行するサイクルを無駄にします。

プログラムでは、最後までクエリをリストに変換するのを遅らせる方が良い場合があるので、LeopardsとHyenasを何度も列挙する場合は、次のようにします。

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();
662
Chris Wenham

Claudio BernasconiのTechBlogには、非常に良い記事があります。 IEnumerable、ICollection、IList、およびList を使用する場合

ここではシナリオと機能に関するいくつかの基本的なポイントを示します。

enter image description hereenter image description here

129

IEnumerableを実装するクラスでは、foreach構文を使用できます。

基本的にコレクションの次のアイテムを取得するメソッドがあります。コレクション全体をメモリに入れる必要はなく、その中にいくつの項目があるのか​​わからないため、foreachは、次の項目を使い果たすまで取得し続けます。

これは、特定の状況では非常に便利です。たとえば、大規模なデータベーステーブルでは、行の処理を開始する前にすべてのものをメモリにコピーする必要はありません。

ListIEnumerableを実装しましたが、メモリ内のコレクション全体を表します。もしあなたがIEnumerableを持っていて、.ToList()を呼ぶならば、あなたはメモリの列挙の内容で新しいリストを作成します。

Linq式は列挙型を返します。デフォルトでは、foreachを使用して繰り返し処理を行うと式が実行されます。 IEnumerable linqステートメントは、foreachを反復するときに実行されますが、.ToList()を使用してより早く反復するように強制することができます。

これが私の言っていることです:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
120
Keith

皮肉なことに、これの複製として閉じられた質問に回答した、1つの重大な違いについて誰も言及しませんでした。

IEnumerableは読み取り専用で、Listは読み取り専用です。

ListとIEnumerableの実際的な違いを参照してください

74
CAD bloke

最も重要なことは、Linqを使用してもクエリがすぐに評価されないことです。これは、結果として得られるIEnumerable<T>foreach内で反復することの一部としてのみ実行されます - それがすべての奇妙な代行者がしていることです。

そのため、最初の例では、ToListを呼び出してクエリ結果をリストに入れることで、クエリをただちに評価します。
2番目の例は、後でクエリを実行するために必要なすべての情報を含むIEnumerable<T>を返します。

パフォーマンスの面では、答えは それは依存性 です。結果を一度に評価する必要がある場合(たとえば、後で問い合わせている構造を変更している場合、またはIEnumerable<T>の繰り返しに長時間をかけたくない場合)、リストを使用してください。それ以外の場合はIEnumerable<T>を使用してください。結果をリストに格納する特別な理由がない限り、デフォルトでは2番目の例のオンデマンド評価を使用する必要があります。

64
thecoop

IEnumerableの利点は、(通常はデータベースを使用した)遅延実行です。実際にデータをループするまで、クエリは実行されません。それは必要とされるまで待つクエリです(別名遅延ロード)。

ToListを呼び出すと、クエリが実行されます。つまり、「実体化」されます。

両方に賛否両論があります。 ToListを呼び出すと、いつクエリが実行されるのかという謎を取り除くことができます。あなたがIEnumerableに固執するならば、あなたはそれが実際に必要とされるまでプログラムが少しの仕事もしないという利点を得ます。

37
Matt Sherman

私は1日に陥ったという1つの誤用された概念を共有します。

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

期待される結果

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

実結果

// what printed actualy   
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari

説明

他の答えと同様に、結果の評価はToListまたは同様の呼び出しメソッド、例えばToArrayを呼び出すまで延期されました。

したがって、この場合のコードを次のように書き直すことができます。

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

アラウンドプレイ

https://repl.it/E8Ki/0

16
amd

あなたがしたいすべてがそれらを列挙である場合は、IEnumerableを使用しています。

列挙されている元のコレクションを変更することは危険な操作であること、しかし、注意してください - この場合には、あなたが最初にToListことになるでしょう。これはIEnumerableを列挙、メモリ内の各要素の新しいリスト要素を作成し、一度だけ列挙している場合ので、パフォーマンスが低いだろう - しかし、より安全な、時にはList方法は、(ランダムアクセスで例えば)便利です。

15
Daren Thomas

上記のすべての回答に加えて、これが私の2セントです。 List以外にも、ICollection、ArrayListなど、IEnumerableを実装する型は他にも多数あります。したがって、IEnumerableを任意のメソッドのパラメーターとして使用すると、コレクション型を関数に渡すことができます。つまり、特定の実装ではなく抽象化を操作する方法があります。

5
Ananth

IEnumerableをListに変換できない場合が多くあります(無限リストや非常に大きなリストなど)。最も明白な例はすべての素数、彼らの詳細を持つfacebookのすべてのユーザー、またはebayの上のすべてのアイテムです。

違いは、 "List"オブジェクトは "今ここで"今すぐ格納されているのに対し、 "IEnumerable"オブジェクトは "一度に1つだけ"動作していることです。だから私がebay上のすべてのアイテムを見ているとしたら、一度に一つずつ小さなコンピューターでも扱えるものになるだろうが、 "。ToList()"は確かに私のコンピューターの大きさに関係なくメモリーを使い果たしてしまう。そのような膨大な量のデータを自分自身で格納し処理することはできません。

0
LongChalk