web-dev-qa-db-ja.com

IEnumerable <T>にキャストするのではなく、なぜ.AsEnumerable()を使用するのですか?

_IEnumerable<T>_の拡張メソッドの1つは.AsEnumerable()です。このメソッドは、呼び出された列挙可能なオブジェクトを_IEnumerable<T>_のインスタンスに変換します。ただし、この拡張メソッドに適用するにはオブジェクトが_IEnumerable<T>_を実装する必要があるため、_IEnumerable<T>_への変換は_IEnumerable<T>_にキャストするだけです。私の質問は、なぜこの方法が存在するのですか?

例:

_List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;
_

上記の例では、_stringsEnum1_と_stringsEnum2_は同等です。拡張メソッドのポイントは何ですか?

編集:当然、_IQueryable<T>_にキャストするときに.AsQueryable()メソッドがあるのはなぜですか?

62
Erik Forbes

ここでは読みやすさが主な問題です。それを考慮してください

Table.AsEnumerable().Where(somePredicate)

よりもはるかに読みやすい

((IEnumerable<TableObject>)Table).Where(somePredicate).

または、SQL Serverでクエリの一部を実行し、残りをメモリで実行することを想像してください。

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

versus

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

さて、そのようなメソッドがなぜ有用なのかについては、LINQ to SQL TableDataContextの例を考えてください。 TableIQueryableであるため、IEnumerableを実装します。このようなWhereTableメソッドを呼び出して結果を列挙すると、最終的にSQL ServerでSQLステートメントが実行されるコードが実行されます。 AsEnumerableとは、いや、LINQ to SQLプロバイダーを使用してWhereを実行したくない、LINQ to Objects実装のWhereを使用したい、ということです。

したがって、列挙

Table.Where(somePredicate)

sQL Serverでクエリが実行されるのに対し、

Table.AsEnumerable().Where(somePredicate)

Tableで表されるテーブルをメモリに取り込み、メモリ内でWhere機能を実行します(SQL Serverではありません!)

これがAsEnumerableのポイントです:IEnumerableメソッドの特定の実装を非表示にして、代わりに標準実装を使用できるようにします。

84
jason

クエリの実装に関連するものの、読みやすさとは別の理由を考えました。別のLinqプロバイダーを介して返される匿名型でのオブジェクトへのLinqの使用。匿名型(または匿名型のコレクション)にキャストすることはできませんが、.AsEnumerable()を使用してキャストを実行できます。

例:

_// Get an IQueryable of anonymous types.
var query = from p in db.PeopleTable /* Assume Linq to SQL */
            select new { Name = p.Name, Age = p.Age };

// Execute the query and pull the results into an IEnumerable of anonymous types
var enum = query.AsEnumerable();

// Use Linq to Objects methods to further refine.
var refined = from p in enum
              select new
              {
                  Name = GetPrettyName(p.Name),
                  DOB = CalculateDOB(p.Age, DateTime.Now)
              };
_

明らかに、ここでの理由は、Linq to SQLのようなものを使用して一部のレコードを匿名型にプルダウンしてから、クライアントでLinq to Objectsを使用してカスタムロジック(Linq to SQLでは不可能)を実行することです。側。

_IEnumerable<_anon>_へのキャストは不可能なので、.AsEnumerable()が唯一の方法です。

私がこれをつなぎ合わせるのを手伝ってくれたみんなに感謝します。 =)

15
Erik Forbes

私は本を​​読んでいる_C# 6.0 in a Nutshell。以下は本のAsEnumerableの例です。


目的は、IQueryable<T>シーケンスからIEnumerable<T>、後続のクエリ演算子を、クエリ可能な演算子の代わりに列挙可能な演算子にバインドするように強制します。これにより、クエリの残りがlocallyを実行します。

たとえば、SQL ServerにMedicalArticlesテーブルがあり、LINQ to SQLまたはEFを使用して、抄録に100語未満のインフルエンザに関するすべての記事を取得したいとします。後者の述語には、正規表現が必要です。

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

var query = dataContext.MedicalArticles
            .Where (article => article.Topic == "influenza" &&
            wordCounter.Matches (article.Abstract).Count < 100);

問題は、SQL Serverが正規表現をサポートしていないため、LINQ-to-dbプロバイダーが例外をスローし、クエリをSQLに変換できないと文句を言うことです。これを解決するには、2つのステップでクエリを実行します。まず、LINQ to SQLクエリを使用してインフルエンザに関するすべての記事を取得し、次に100語未満の抄録をローカルでフィルタリングします。

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
    .Where (article => article.Topic == "influenza");

IEnumerable<MedicalArticle> localQuery = sqlQuery
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

AsEnumerableを使用すると、1つのクエリで同じことができます。

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

AsEnumerableを呼び出す代わりに、ToArrayまたはToListを呼び出します。 AsEnumerableの利点は、クエリの即時実行を強制しないことですまた、ストレージ構造を作成しません。

4
Timeless

Linq拡張メソッドと同じ名前のメソッドがオブジェクトにある場合、拡張メソッドは非表示になります。 AsEnumerableを使用すると、拡張機能を取得できます。

これはSP1の新機能のようです。

昨日、データテーブルからメンバー識別子を抽出するコード行がありました。

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

sP1をインストールするまで問題なく動作しました。今ではそれが読まない限り機能しません

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

編集:本当の理由が見つかりました

これにより、同じlinqステートメントでリモートメソッド(SQLステートメントのWHEREなど)とローカルメソッドの両方を使用できます。 AsEnumerableを使用しない場合(つまり、キャストするだけ)、クエリジェネレーターは、ローカルメソッドを含むリモート実行用の式ツリーを作成しようとします。 AsEnumerableをクエリに含めると、そのクエリの残りの部分がリモートクエリの結果に対してローカルで実行されます。

から https://msdn.Microsoft.com/en-us/library/bb335435(v = vs.110).aspx

データベーステーブルを表すテーブルタイプには、述語引数を式ツリーとして受け取り、リモート実行のためにツリーをSQLに変換するWhereメソッドを含めることができます。たとえば、述語がローカルメソッドを呼び出すためにリモート実行が望ましくない場合、AsEnumerableメソッドを使用してカスタムメソッドを非表示にし、代わりに標準クエリ演算子を使用可能にすることができます。

3

これは、IEnumerableにキャストするための最も素敵で最短の方法です。 Reflectorで見ると、オブジェクトをIEnumerableとして返す以外は何もしないことがわかります。

MSDNから:

AsEnumerable(Of TSource)(IEnumerable(Of TSource))メソッドは、ソースのコンパイル時タイプを、IEnumerable(Of T)を実装するタイプからIEnumerable(Of T)自体に変更する以外の効果はありません。

3
Meta-Knight

匿名型は、これらの種類の拡張メソッドを提供する主な理由です。 (ジェネリックパラメーターで匿名型を使用することはできません)しかし、メソッド呼び出しでは型推論を使用できるため、ジェネリックパラメーターでの型の指定を省略できます。

3
james

あなたが言うように、型がすでにIEnumerable<T>を実装している場合、インターフェースへのキャストとAsEnumerableメソッドの呼び出しの間に実際には機能的な違いはありません。

私の推測は、推測に過ぎませんが、AsEnumerableを呼び出すと読みやすさが向上し、他のLINQ拡張メソッドの流fluentな署名が保持されます。

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

また、IEnumerable<T>を実装しない型を許可します- たとえば、DataTable -独自のバージョンのAsEnumerable拡張を持つことができます。これにより、これらの型に対するクエリで同じパターンを使用し続けることができます-呼び出している別のAsEnumerableメソッドであっても心配する必要なしに型が本当にIEnumerable<T>を実装しているかどうか。

2
LukeH