传递lambda表达式代替IComparer或IEqualityComparer或任何单方法接口?

Wat*_* v2 56 linq ienumerable extension-methods icomparer iequalitycomparer

我碰巧看到了一些代码,其中这个人将lambda表达式传递给ArrayList.Sort(这里是IComparer)或IEnumerable.SequenceEqual(IEnumerable列表,IEqualityComparer here),其中需要IComparer或IEqualityComparer.

我不能确定我是否看过它,或者我只是在做梦.我似乎无法在这些集合中找到任何接受Func <>或其方法签名中的委托的扩展.

有这样的过载/扩展方法吗?或者,如果没有,是否有可能像这样捣乱并传递一个算法(读委托),其中预期单方法接口?

更新 谢谢大家.那正是我所想.我一定是在做梦.我知道如何编写转换.我只是不确定我是否见过这样的东西,或者只是觉得我已经看过了.

又一次更新 看,在这里,我找到了一个这样的实例.毕竟我并没有做梦.看看这家伙这做什么.是什么赋予了?

这是另一个更新: 好的,我明白了.那家伙正在使用Comparison<T>超载.尼斯.很好,但完全容易误导你.不过很好.谢谢.

Aci*_*kie 24

我也在谷歌搜索网络寻求解决方案,但我没有找到任何令人满意的解决方案.所以我创建了一个通用的EqualityComparerFactory:

using System;
using System.Collections.Generic;

/// <summary>
/// Utility class for creating <see cref="IEqualityComparer{T}"/> instances 
/// from Lambda expressions.
/// </summary>
public static class EqualityComparerFactory
{
    /// <summary>Creates the specified <see cref="IEqualityComparer{T}" />.</summary>
    /// <typeparam name="T">The type to compare.</typeparam>
    /// <param name="getHashCode">The get hash code delegate.</param>
    /// <param name="equals">The equals delegate.</param>
    /// <returns>An instance of <see cref="IEqualityComparer{T}" />.</returns>
    public static IEqualityComparer<T> Create<T>(
        Func<T, int> getHashCode,
        Func<T, T, bool> equals)
    {
        if (getHashCode == null)
        {
            throw new ArgumentNullException(nameof(getHashCode));
        }

        if (equals == null)
        {
            throw new ArgumentNullException(nameof(equals));
        }

        return new Comparer<T>(getHashCode, equals);
    }

    private class Comparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, int> _getHashCode;
        private readonly Func<T, T, bool> _equals;

        public Comparer(Func<T, int> getHashCode, Func<T, T, bool> equals)
        {
            _getHashCode = getHashCode;
            _equals = equals;
        }

        public bool Equals(T x, T y) => _equals(x, y);

        public int GetHashCode(T obj) => _getHashCode(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是,CreateComparer方法有两个参数:GetHashCode(T)的委托和Equals(T,T)的委托

例:

class Person
{
    public int Id { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var list1 = new List<Person>(new[]{
            new Person { Id = 1, FirstName = "Walter", LastName = "White" },
            new Person { Id = 2, FirstName = "Jesse", LastName = "Pinkman" },
            new Person { Id = 3, FirstName = "Skyler", LastName = "White" },
            new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
        });

        var list2 = new List<Person>(new[]{
            new Person { Id = 1, FirstName = "Walter", LastName = "White" },
            new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
        });


        // We're comparing based on the Id property
        var comparer = EqualityComparerFactory.Create<Person>(
            a => a.Id.GetHashCode(),
            (a, b) => a.Id==b.Id);
        var intersection = list1.Intersect(list2, comparer).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)


Xos*_*uis 22

我不太确定它到底有什么用处,因为我认为在Base Library中大多数情况下期望IComparer有一个需要比较的重载...但只是为了记录:

在.Net 4.5中,他们添加了一种从比较:Comparer.Create获取IComparer的方法

所以你可以将lambda传递给它并获得IComparer.

  • EqualityComparer有类似的东西吗?它没有Create方法,但为Comparer而不是EqualityComparer添加这个有用的方法似乎很奇怪 (5认同)
  • @rdans`EqualityComparer`使用不同的签名,并且还使用`GetHashCode`方法.所以你不能轻易为此创建比较器. (2认同)

Ant*_*ram 11

您可以为Array.Sort方法提供lambda,因为它需要一个接受两个T类型对象并返回整数的方法.因此,您可以提供以下定义的lambda (a, b) => a.CompareTo(b).下一个整数数组的示例:

int[] array = { 1, 8, 19, 4 };

// descending sort 
Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
Run Code Online (Sandbox Code Playgroud)

  • @Stripling,我相信这实际上是使用接受`Comparison <T>`的重载,因为`Comparison`是一个接受这两个参数并返回整数的委托.因此,提供有效的lambda有资格获得此重载. (6认同)

STO*_*STO 6

public class Comparer2<T, TKey> : IComparer<T>, IEqualityComparer<T>
{
    private readonly Expression<Func<T, TKey>> _KeyExpr;
    private readonly Func<T, TKey> _CompiledFunc
    // Constructor
    public Comparer2(Expression<Func<T, TKey>> getKey)
    {
        _KeyExpr = getKey;
        _CompiledFunc = _KeyExpr.Compile();
    } 

    public int Compare(T obj1, T obj2)
    {
        return Comparer<TKey>.Default.Compare(_CompiledFunc(obj1), _CompiledFunc(obj2));
    }

    public bool Equals(T obj1, T obj2)
    { 
        return EqualityComparer<TKey>.Default.Equals(_CompiledFunc(obj1), _CompiledFunc(obj2));
    }

    public int GetHashCode(T obj)
    {
         return EqualityComparer<TKey>.Default.GetHashCode(_CompiledFunc(obj));
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它

ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
Run Code Online (Sandbox Code Playgroud)

  • 为什么不接受`Func <T,TKey>`? (3认同)

Ste*_*han 5

您不能直接传递它,但是可以通过定义一个LambdaComparer除a之外的类Func<T,T,int>然后在它中使用来实现CompareTo

它不够简洁,但是您可以通过上一些创新的扩展方法来使其更短Func


cod*_*zen 3

我投票支持梦想理论。

您不能在需要对象的地方传递函数:System.Delegate 的衍生物(这就是 lambda)不实现这些接口。

您可能看到的是委托的使用Converter<TInput, TOutput>,它可以通过 lambda 进行建模。Array.ConvertAll使用此委托的实例。