タイトルは質問をほぼ要約しています。私はそれに何の問題もありません。そのデザインの選択の背後にある理由に興味があるだけです.
3 に答える
私の推測?さまざまなプロバイダーに対するシンプルさと互換性。
他のいくつかの回答とは対照的に、これは遅延実行とは関係ありません。これは重要な概念ですが、問題とは無関係です。たとえば、次の完全に有効なメソッドを作成できます。
public static IEnumerable<T> NotBuffered<T>(this IEnumerable<T> input)
{
return (IEnumerable<T>)input.ToList(); //not deferred
}
WhereEnumerable
または、 aと同じように機能するIEnumerable
が、次のプロパティを持つa を公開することもできます。
WhereEnumerable data = source.Where(x=> x.Name == "Cheese"); //still deferred
print(data.First());
print(data.skipped); //Number of items that failed the test.
print(data.returned); //Number of items that passed the test.
そして、これはおそらく有用であり、実証されているように、基本的なLinqToObjects実装で簡単に実装できます。ただし、 LinqToSQL 、 LinqToMongo、またはLinqToOpenCLドライバーで同じ機能を実装することは、かなり難しいか不可能かもしれません。これにより、実装間でのコードの移植性が低下し、実装者の複雑さが増します。
たとえば、MongoDB はサーバー上で (特殊なクエリ言語で) クエリを実行し、これらの統計をユーザーが利用できるようにしません。さらに、インデックスなどの概念を使用すると、これらの概念は無意味になる可能性があります。たとえばusers.Where(user => user.ID = "{ID"}).First()
、インデックスでは、結果を見つける前に 0 レコードを「スキップ」する可能性があります。インデックス内の 100,412 またはディスクまたはインデックス ノード 431 の 40,231 の位置にある場合でも、それは 'シンプルな問題...
最後に、必要に応じて独自の LINQ メソッドを記述して、この機能を使用して独自のカスタム型を返すか、「統計」オブジェクトなどを出力するオーバーロードを介していつでも作成できます。後者の架空の例:
var stats = new WhereStats();
WhereEnumerable data = source.Where(x=> x.Name == "Cheese", stats);
print(data.First());
print(stats.skipped); //Number of items that failed the test.
print(stats.returned); //Number of items that passed the test.
編集: 入力された where の例 (概念実証のみ):
using System;
using System.Collections.Generic;
using System.Linq;
namespace TypedWhereExample
{
class Program
{
static void Main(string[] args)
{
var data = Enumerable.Range(0, 1000);
var typedWhere1 = data.TypedWhere(x => x % 2 == 0);
var typedWhere2 = typedWhere1.TypedWhere(x => x % 3 == 0);
var result = typedWhere2.Take(10).ToList(); //Works like usual Linq
//But returns additional data
Console.WriteLine("Result: " + string.Join(",", result));
Console.WriteLine("Typed Where 1 Skipped: " + typedWhere1.Skipped);
Console.WriteLine("Typed Where 1 Returned: " + typedWhere1.Returned);
Console.WriteLine("Typed Where 2 Skipped: " + typedWhere2.Skipped);
Console.WriteLine("Typed Where 2 Returned: " + typedWhere2.Returned);
Console.ReadLine();
//Result: 0,6,12,18,24,30,36,42,48,54
//Typed Where 1 Skipped: 27
//Typed Where 1 Returned: 28
//Typed Where 2 Skipped: 18
//Typed Where 2 Returned: 10
}
}
public static class MyLINQ
{
public static TypedWhereEnumerable<T> TypedWhere<T>
(this IEnumerable<T> source, Func<T, bool> filter)
{
return new TypedWhereEnumerable<T>(source, filter);
}
}
public class TypedWhereEnumerable<T> : IEnumerable<T>
{
IEnumerable<T> source;
Func<T, bool> filter;
public int Skipped { get; private set; }
public int Returned { get; private set; }
public TypedWhereEnumerable(IEnumerable<T> source, Func<T, bool> filter)
{
this.source = source;
this.filter = filter;
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
foreach (var o in source)
if (filter(o)) { Returned++; yield return o; }
else Skipped++;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
foreach (var o in source)
if (filter(o)) { Returned++; yield return o; }
else Skipped++;
}
}
}
あなたの質問を正しく理解できるようにするために、例を使用します。
この方法を取ります:
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
あなたの質問は次のとおりだと思います。IEnumerable<TSource>
たとえば、返されないのはなぜEnumerable.WhereEnumerableIterator<TSource>
ですか?
上記の型は、返されるオブジェクトの実際の実行時型ですが、メソッドは単純にそれを と宣言していることに注意してIEnumerable<TSource>
ください。
答えは、それ以外の方法では実質的に何のメリットもありませんが、ゼロではないコストが発生するということです。費用が利益よりも高い場合は、それを行わないでください。
メリットがないのはなぜ?
まず第一に、WhereEnumerableIterator<TSource>
まだ静的に型付けされている多くのオブジェクトがあるためIEnumerable<TSource>
です。その結果、メソッドのオーバーロードは機能せず、現在と同じように、Where メソッドは、シーケンスWhereEnumerableIterator<TSource>
を最適化する場合、その入力を a にキャストしようとする必要があります。.Where(...).Where(...)
これにはいくつかの理由がありますが、そのうちの 1 つが次のパターンです。
IEnumerable<whatever> q = source;
if (!string.IsNullOrEmpty(searchText))
{
q = q.Where(item => item.Name.Contains(searchText));
}
if (startDate.HasValue)
{
// What is the runtime type of q? And what is its compiletime type?
q = q.Where(item => item.Date > startDate.Value);
}
ゼロ以外のコストは、メンテナンスと文書化のコスト (実装を公開すると変更が難しくなり、文書化する必要があります)、およびユーザーにとっての複雑さの増加で構成されます。