8

OrderByLINQ ステートメントで匿名オブジェクトを操作しようとしましたが、今のところ失敗しています。

私はすでにこれらをチェックしました:
Anonymous IComparer implementation
C# linq sort - IComparer をインスタンス化する簡単な方法
C# の特定のフィールドでオブジェクトの配列をソートする方法は?

さまざまなアプローチを試すのに数時間を費やしましたが、欠けているものがあるはずです。

次のクラスがあるとします。

public class Product
{
   public int Id {get; set;}
   public string Name {get; set;}
   public int Popularity {get; set;}
   public decimal Price {get; set;}
}

Andproductsはこれらのオブジェクトのリストです。

匿名オブジェクトで機能するように、この LINQ ステートメントを完成させるにはどうすればよいですか?
明確にするために、私はこれを別の方法で行うことができることを知っていますが、この特定の例を機能させる方法を学ぶことに非常に興味があります.

var sortedProducts = products
                       .OrderBy(p => 
                              new {p.Popularity, p.Price}, 
                              [IComparer magic goes here]);

次の実装で可能になるはずですProjectionComparer:

これを行う方法はありますか?

アップデート:

私はこれについて簡単なパフォーマンス テストを行いました - 匿名の比較ソリューションと標準の orderby.thenby の比較で、匿名のソリューションは非常に遅いようです。

         numProd  | Anon    | chained orderby clauses
         10 000   | 47 ms   | 31 ms
         100 000  | 468 ms  | 234 ms
         1 000 000| 5818 ms | 2387 ms
         5 000 000| 29547 ms| 12105 ms
4

3 に答える 3

8

IComparer<T>比較のために指定したデリゲートを使用する実装を作成し、型推論でインスタンス化できます (「例によるキャスト」に似ています)。

static class AnonymousComparer
{
    public static IComparer<T> GetComparer<T>(T example, Comparison<T> comparison)
    {
        return new ComparerImpl<T>(comparison);
    }
    private class ComparerImpl<T> : IComparer<T>
    {
        private readonly Comparison<T> _comparison;
        public ComparerImpl(Comparison<T> comparison) { _comparison = comparison; }
        public int Compare(T x, T y) { return _comparison.Invoke(x, y); }
    }
}

そして、次のように使用します。

var comparer = AnonymousComparer.GetComparer(
    new { Popularity = 0, Price = 0m },
    (a, b) => //comparison logic goes here
    );

var sortedProducts = products
    .OrderBy(p =>
        new { p.Popularity, p.Price },
        comparer); 

編集:リンク先の投影比較ページをチェックアウトしました。そのアプローチでは、型推論のための "example" 引数は必要ありません。ただし、インターフェイスの代わりにデリゲートを使用するようにするには、このアプローチを適応させる必要があります。ここにあります:

//adapted from http://code.google.com/p/edulinq/source/browse/src/Edulinq/ProjectionComparer.cs?r=0c583631b709679831c99df2646fc9adb781b2be
static class AnonymousProjectionComparer
{
    private class ProjectionComparer<TElement, TKey> : IComparer<TElement>
    {
        private readonly Func<TElement, TKey> keySelector;
        private readonly Comparison<TKey> comparison;

        internal ProjectionComparer(Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
        {
            this.keySelector = keySelector;
            this.comparison = comparison ?? Comparer<TKey>.Default.Compare;
        }

        public int Compare(TElement x, TElement y)
        {
            TKey keyX = keySelector(x);
            TKey keyY = keySelector(y);
            return comparison.Invoke(keyX, keyY);
        }
    }

    public static IComparer<TElement> GetComparer<TElement, TKey>(Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
    {
        return new ProjectionComparer<TElement, TKey>(keySelector, comparison);
    }
}
于 2012-04-27T19:59:23.153 に答える
5

これらのオブジェクトを人気の降順、次に価格順に並べ替えるために、匿名オブジェクトは実際には必要ありません。次のように、OrerBy と ThenBy を組み合わせて使用​​できます。

var sortedProducts = products.OrderByDescending(p => p.Popularity)
    .ThenBy(p => p.Price);

匿名型に対して行うにはIComparer<T>、ファクトリを使用してデリゲートから構築し、型推論を使用することをお勧めします (推論なしで匿名型を指定するのは面倒です!)。

純粋に順序付けのために匿名オブジェクトを作成することのパフォーマンスへの影響を測定したい場合がありますが、Phoogs の回答はデリゲートを使用してオンザフライでComparison<T>構築する良い方法を提供します..IComparer<T>

于 2012-04-27T19:53:30.553 に答える
0

正確な答えではありません...しかし、コメントするには長すぎます:賢明なジェネリック比較子を作成するのは難しいです。

単一のプロパティによるオブジェクトの比較関係は十分に確立されていますが、複数または 2 つのプロパティについてはそのようなことはありません。つまり、平面上のポイントを並べ替えようとするとき、これは非常に一般的な問題です: 2 つの値 (x,y) だけですが、(x1,y1) < (x2,y2) と言う方法がないため、誰もがそれに同意します。

ほとんどの場合、属性 2 ではなく、属性 1 で順序を指定するか、すべての属性を単一の値にマッピングする (つまり、単純にすべての属性を乗算する) ことになります。これらのアプローチは、LINQ で一般的な比較子を必要とせずに簡単に表現できます。

  • チェーン OrderBy(attr1).OrderBy(attr2).... を使用した属性による並べ替え
  • メトリック OrderBy(attr1 * attr2) (またはオブジェクトのその他のメトリック) による順序付け
于 2012-04-27T19:54:22.207 に答える