Pass a lambda expression in place of IComparer or IEqualityComparer or any single-method interface?

后端 未结 8 1931
闹比i
闹比i 2020-12-08 08:57

I happened to have seen some code where this guy passed a lambda expression to a ArrayList.Sort(IComparer here) or a IEnumerable.SequenceEqual(IEnumerable list, IEqualityCom

相关标签:
8条回答
  • 2020-12-08 09:31
    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));
        }
    }
    

    use it like this

    ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
    
    0 讨论(0)
  • 2020-12-08 09:31

    In case if you need this function for use with lambda and possibly two different element types:

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 09:34

    These methods don't have overloads that accept a delegate instead of an interface, but:

    • You can normally return a simpler sort key through the delegate you pass to Enumerable.OrderBy
    • Likewise, you could call Enumerable.Select before calling Enumerable.SequenceEqual
    • It should be straightforward to write a wrapper that implements IEqualityComparer<T> in terms of Func<T, T, bool>
    • F# lets you implement this sort of interface in terms of a lambda :)
    0 讨论(0)
  • 2020-12-08 09:35

    You can't pass it directly however you could do so by defining a LambdaComparer class that excepts a Func<T,T,int> and then uses that in it's CompareTo.

    It is not quite as concise but you could make it shorter through some creative extension methods on Func.

    0 讨论(0)
  • 2020-12-08 09:37

    You can provide a lambda for a Array.Sort method, as it requires a method that accepts two objects of type T and returns an integer. As such, you could provide a lambda of the following definition (a, b) => a.CompareTo(b). An example to do a descending sort of an integer array:

    int[] array = { 1, 8, 19, 4 };
    
    // descending sort 
    Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
    
    0 讨论(0)
  • 2020-12-08 09:41

    I vote for the dreaming theory.

    You can't pass a function where an object is expected: derivatives of System.Delegate (which is what lambdas are) don't implement those interfaces.

    What you probably saw is a use of the of the Converter<TInput, TOutput> delegate, which can be modeled by a lambda. Array.ConvertAll uses an instance of this delegate.

    0 讨论(0)
提交回复
热议问题