LINQ OrderBy具有投影比较器的匿名对象

Joa*_*rks 8 c# linq icomparer

我一直在尝试OrderBy使用LINQ语句来处理匿名对象,但现在已经失败了.

我已经检查了这些:
匿名IComparer实现
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;}
}
Run Code Online (Sandbox Code Playgroud)

并且products是这些对象的列表.

如何完成此LINQ语句,以便它可以与匿名对象一起使用?
要清楚,我知道我可以用不同的方式做到这一点,但我非常有兴趣学习如何使这个特定的例子有效.

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

似乎应该可以通过以下方式实现ProjectionComparer:http:
//code.google.com/p/edulinq/source/browse/src/Edulinq/ProjectionComparer.cs?r = 0c583631b709679831c99df2646fc9adb781b2be

任何想法如何做到这一点?

更新:

我对此进行了快速的性能测试 - 匿名比较器解决方案vs标准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
Run Code Online (Sandbox Code Playgroud)

pho*_*oog 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); }
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它:

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); 
Run Code Online (Sandbox Code Playgroud)

编辑:我刚刚查看了您链接到的投影比较器页面.使用该方法,您不需要类型推断的"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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Jam*_*are 5

您实际上并不需要匿名对象来按流行度降序然后定价的顺序来订购这些对象,可以结合使用OrerBy和ThenBy,例如:

var sortedProducts = products.OrderByDescending(p => p.Popularity)
    .ThenBy(p => p.Price);
Run Code Online (Sandbox Code Playgroud)

要IComparer<T>对匿名类型进行处理,最好使用工厂从委托构造一个匿名类型并使用类型推断(指定没有推断的匿名类型很痛苦!)。

您可能想衡量纯粹为了订购而创建匿名对象的性能影响,但是Phoogs的回答提供了一种使用Comparison<T>委托来动态构建委托的好方法IComparer<T>。