36

SortMethod列挙型で指定されたいくつかのタイプの並べ替え(つまり、さまざまなプロパティによる並べ替え)に基づいてIQueryableを並べ替える拡張メソッドがあるとします。

public static IOrderedEnumerable<AClass> OrderByX(this IQueryable<AClass> values,
    SortMethod? sortMethod)
{ 
    IOrderedEnumerable<AClass> queryRes = null;
    switch (sortMethod)
    {
        case SortMethod.Method1:
            queryRes = values.OrderBy(a => a.Property1);
            break;
        case SortMethod.Method2:
            queryRes = values.OrderBy(a => a.Property2);
            break;
        case null:
            queryRes = values.OrderBy(a => a.DefaultProperty);
            break;
        default:
            queryRes = values.OrderBy(a => a.DefaultProperty);
            break;
    }
    return queryRes;
}

sortMethodisの場合null(つまり、値の順序を気にしないように指定されている場合)、デフォルトのプロパティで順序付けする代わりに、IEnumerator値を「順序付けられた」ものとして渡す方法はありますか?実際の並べ替えを実行する必要がありますか?

この拡張機能を呼び出して、追加のThenBy注文を実行できるようにしたいと思います。

4

3 に答える 3

53

デフォルトの場合に行う必要があるのは次のとおりです。

queryRes = values.OrderBy(a => 1);

これは事実上、noopソートになります。OrderByは安定したソートを実行するため、選択したオブジェクトが等しい場合でも元の順序が維持されます。これはであり、IQueryableではないIEnumerableため、クエリプロバイダーが安定した並べ替えを実行しない可能性があることに注意してください。その場合、順序を維持することが重要かどうか、または「結果を呼び出すことができる限り、結果がどのような順序であるかは関係ありません」と言うのが適切かどうかを知る必要がありますThenBy

実際の並べ替えを回避できる別のオプションは、独自のIOrderedEnumerable実装を作成することです。

public class NoopOrder<T> : IOrderedEnumerable<T>
{
    private IQueryable<T> source;
    public NoopOrder(IQueryable<T> source)
    {
        this.source = source;
    }

    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
        if (descending)
        {
            return source.OrderByDescending(keySelector, comparer);
        }
        else
        {
            return source.OrderBy(keySelector, comparer);
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return source.GetEnumerator();
    }
}

これにより、クエリは次のようになります。

queryRes = new NoopOrder<AClass>(values);

上記のクラスの結果は、それへの呼び出しがある場合、ThenBy事実ThenBy上トップレベルのソートになることに注意してください。それは事実上、後続ThenByOrderBy呼び出しに変えています。(これは驚くべきことではありません。メソッドThenByを呼び出します。CreateOrderedEnumerableそこでこのコードはを呼び出しOrderBy、基本的にそれをThenByに変換しOrderByます。概念的な並べ替えの観点から、これは「このシーケンスのすべてのアイテムはこの種の目には等しいが、等しいオブジェクトが他の何かによってタイブレイクされるべきであると指定する場合は、そうする。

「noopsort」の別の考え方は、入力シーケンスのインデックスに基づいてアイテムを並べ替えることです。これは、アイテムがすべて「等しい」わけではないことを意味します。つまり、順序入力シーケンス出力シーケンスの最終順序になり、入力シーケンスの各アイテムは常に前のアイテムよりも大きいため、「タイブレーカー」が追加されます。 「比較は何も行わず、その後のThenBy呼び出しは無意味になります。この動作が必要な場合は、前の動作よりも実装がさらに簡単です。

public class NoopOrder<T> : IOrderedEnumerable<T>
{
    private IQueryable<T> source;
    public NoopOrder(IQueryable<T> source)
    {
        this.source = source;
    }

    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
        return new NoopOrder<T>(source);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return source.GetEnumerator();
    }
}
于 2013-01-18T17:27:45.750 に答える
5

常に同じインデックス値を返す場合、元のリストの順序を保持するIOrderedEnumerableを取得します。

case null:
     queryRes = values.OrderBy(a => 1);
     break;

ところで、これは正しいことではないと思います。注文が禁止されているコレクションを入手できますが、実際にはそうではありません。

于 2013-01-18T17:26:58.820 に答える
-1

要するに、IOrderedEnumerable は、OrderBy()/ThenBy() メソッドに文法構造を提供するためだけに存在し、ThenBy() で順序付け句を開始しようとするのを防ぎます。処理する。OrderBy() によって実際に順序付けされた場合を除き、順序付けされたコレクションを識別する「マーカー」を意図したものではありません。したがって、答えは、並べ替えメソッドが null であることが、列挙型が何らかの「デフォルトの順序」にあることを示していると思われる場合は、そのデフォルトの順序を指定する必要があるということです (現在の実装のように)。SortingMethod を指定しないことで、列挙型が「何も順序付けられていない」と推測し、実際の順序を気にしない場合でも、実際には順序付けられていないのに、列挙型が順序付けられていると述べるのは不誠実です。

インターフェイスを使用してコレクションを順序付けされたものとして単純にマークしようとすることに固有の「問題」は、単純な並べ替え以上のプロセスがあることです。などの順序付けメソッド チェーンを実行することmyCollection.OrderBy().ThenBy().ThenByDescending()で、呼び出しごとにコレクションを実際に並べ替えるわけではありません。とにかくまだ。代わりに、OrderedEnumerable という名前の「反復子」クラスの動作を定義しています。このクラスは、チェーンで定義したプロジェクションと比較を使用して、実際の並べ替えられた要素が必要なときに並べ替えを実行します。

OrderBy(x=>1) はヌープであり、SQL プロバイダーから最適化する必要があると述べている Servy の回答は、Enumerable に対して行われたこの呼び出しがまだかなりの作業を行うという現実を無視しており、ほとんどの SQL プロバイダーは実際、この種の呼び出しは最適化されません。OrderBy(x=>1) は、ほとんどの Linq プロバイダーで、"ORDER BY 1" 句を含むクエリを生成します。これは、SQL プロバイダーに独自の並べ替えを強制するだけでなく、実際には順序の変更をもたらします。 T-SQLでは、少なくとも「ORDER BY 1」は選択リストの最初の列で並べ替えることを意味するためです。

于 2013-01-18T18:20:12.190 に答える