使用 lambda 表达式代替 IComparer 参数

Posted

技术标签:

【中文标题】使用 lambda 表达式代替 IComparer 参数【英文标题】:Using lambda expression in place of IComparer argument 【发布时间】:2013-05-26 05:22:31 【问题描述】:

C# 是否可以在方法调用中将 lambda 表达式作为 IComparer 参数传递?

比如

var x = someIEnumerable.OrderBy(aClass e => e.someProperty, 
(aClass x, aClass y) => 
  x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0);

我不能完全编译它,所以我猜不是,但 lambda 和匿名委托之间的协同作用似乎如此明显,我觉得我一定是在做一些愚蠢的错误。

TIA

【问题讨论】:

这里可能的答案:***.com/questions/9824435/… 【参考方案1】:

如果您使用的是 .NET 4.5,则可以使用静态方法 Comparer&lt;aClass&gt;.Create

文档:Comparer&lt;T&gt;.Create Method 。

例子:

var x = someIEnumerable.OrderBy(e => e.someProperty, 
    Comparer<aClass>.Create((x, y) => x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0)
    );

【讨论】:

遗憾的是,我们在 .Net 3.5 土地上苦苦挣扎!买不起将 TFS 升级到最新版本所需的超大楔子:-( @haughtonomous 如果这是唯一阻碍你的事情,你是否考虑过放弃 TFS 以支持其他东西? 你知道为什么我们不能将 lambda 直接放在那里,而是需要一个包装器的基本理论(不像“因为它需要 lambda 以外的类型”)吗? @jw_ 我不确定这背后有多少理论。 .OrderBy (Linq) 的作者决定不使用接受委托进行比较的重载(如 Comparison&lt;TKey&gt; 委托)。如果需要,您可以创建自己的扩展方法。 理论上似乎是接口有2+方法。【参考方案2】:

正如 Jeppe 指出的,如果您使用的是 .NET 4.5,则可以使用静态方法 Comparer&lt;T&gt;.Create

如果不是,这应该是等效的实现:

public class FunctionalComparer<T> : IComparer<T>

    private Func<T, T, int> comparer;
    public FunctionalComparer(Func<T, T, int> comparer)
    
        this.comparer = comparer;
    
    public static IComparer<T> Create(Func<T, T, int> comparer)
    
        return new FunctionalComparer<T>(comparer);
    
    public int Compare(T x, T y)
    
        return comparer(x, y);
    

【讨论】:

可能想给这个类一个不同的名字以避免与库的类冲突。 语法细节:泛型类的构造函数不能包含类名的&lt;T&gt;部分。【参考方案3】:

如果您始终想要比较投影键(例如单个属性),您可以定义一个类,为您封装所有键比较逻辑,包括空检查、两个对象上的键提取以及使用指定的键比较或默认的内部比较器:

public class KeyComparer<TSource, TKey> : Comparer<TSource>

    private readonly Func<TSource, TKey> _keySelector;
    private readonly IComparer<TKey> _innerComparer;

    public KeyComparer(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    
        _keySelector = keySelector;
        _innerComparer = innerComparer ?? Comparer<TKey>.Default;
    

    public override int Compare(TSource x, TSource y)
    
        if (object.ReferenceEquals(x, y))
            return 0;
        if (x == null)
            return -1;
        if (y == null)
            return 1;

        TKey xKey = _keySelector(x);
        TKey yKey = _keySelector(y);
        return _innerComparer.Compare(xKey, yKey);
    

为方便起见,工厂方法:

public static class KeyComparer

    public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    
        return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
    

然后你可以像这样使用它:

var sortedSet = new SortedSet<MyClass>(KeyComparer.Create((MyClass o) => o.MyProperty));

您可以参考我的blog post,了解有关此实现的详细讨论。

【讨论】:

以上是关于使用 lambda 表达式代替 IComparer 参数的主要内容,如果未能解决你的问题,请参考以下文章

编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法Lambda表达式代替方法

编写高质量代码改善C#程序的157个建议——建议37:使用Lambda表达式代替方法和匿名方法

Lambda表达式代替左连接

10.Lambda表达式入门

Java-Lambda表达式第一篇认识Lambda表达式

Lambda表达式