72

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

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

有这样的重载/扩展方法吗?或者,如果没有,是否有可能像这样乱七八糟地传递一个算法(读取委托),其中需要一个单方法接口?

更新 谢谢大家。我也那么认为。我一定是在做梦。我知道如何编写转换。我只是不确定我是否看到过类似的东西,或者只是认为我看到过。

另一个更新 看,在这里,我发现了一个这样的例子。毕竟我不是在做梦。看看这家伙在这里做什么。是什么赋予了?

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

4

8 回答 8

33

我不太确定它到底有什么用,因为我认为对于 Base Library 中期望 IComparer 的大多数情况,存在需要比较的重载......但仅供记录:

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

所以你可以将你的 lambda 传递给它并获得一个 IComparer。

于 2012-06-11T10:03:59.250 回答
33

我也在网上搜索解决方案,但我没有找到任何令人满意的解决方案。所以我创建了一个通用的 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);
    }
}

这个想法是,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();
    }
}
于 2013-09-24T14:18:56.150 回答
13

您可以为 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));
于 2010-07-06T20:18:01.767 回答
7
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));
    }
}

像这样使用它

ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
于 2010-07-06T20:22:50.750 回答
5

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

它不是那么简洁,但您可以通过一些创造性的扩展方法使其更短Func

于 2010-07-06T20:21:42.487 回答
4

这些方法没有接受委托而不是接口的重载,但是:

  • 您通常可以通过传递给的委托返回一个更简单的排序键Enumerable.OrderBy
  • 同样,您可以在调用Enumerable.Select之前先调用Enumerable.SequenceEqual
  • IEqualityComparer<T>编写一个按照以下方式实现的包装器应该很简单Func<T, T, bool>
  • F# 允许您根据 lambda 实现这种接口 :)
于 2010-07-06T20:19:46.183 回答
3

我投票赞成做梦理论。

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

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

于 2010-07-06T20:17:20.453 回答
3

如果您需要此函数与 lambda 和可能的两种不同元素类型一起使用:

static class IEnumerableExtensions
{
    public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> comparer)
    {
        if (first == null)
            throw new NullReferenceException("first");

        if (second == null)
            throw new NullReferenceException("second");

        using (IEnumerator<T1> e1 = first.GetEnumerator())
        using (IEnumerator<T2> e2 = second.GetEnumerator())
        {
            while (e1.MoveNext())
            {
                if (!(e2.MoveNext() && comparer(e1.Current, e2.Current)))
                    return false;
            }

            if (e2.MoveNext())
                return false;
        }

        return true;
    }
}
于 2016-07-26T16:13:29.783 回答