views:

327

answers:

5

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, IEqualityComparer here) where an IComparer or an IEqualityComparer was expected.

I can't be sure if I saw it though, or I am just dreaming. And I can't seem to find an extension on any of these collections that accepts a Func<> or a delegate in their method signatures.

Is there such an overload/extension method? Or, if not, is it possible to muck around like this and pass an algorithm (read delegate) where a single-method interface is expected?

Update Thanks, everyone. That's what I thought. I must've been dreaming. I know how to write a conversion. I just wasn't sure if I'd seen something like that or just thought I'd seen it.

Yet another update Look, here, I found one such instance. I wasn't dreaming after all. Look at what this guy is doing here. What gives?

And here's another update: Ok, I get it. The guy's using the Comparison<T> overload. Nice. Nice, but totally prone to mislead you. Nice, though. Thanks.

A: 

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.

codekaizen
+2  A: 

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));
Anthony Pegram
Can you explain this further? Is this a behavior unique to the IComparer<T> interface, or will it work on any interface that only has a single method on it?
StriplingWarrior
@Stripling, I believe this is actually using the overload that accepts a `Comparison<T>`, as `Comparison` is a delegate that accepts the two parameters and returns the integer. As such, providing a valid lambda qualifies for this overload.
Anthony Pegram
A: 

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 :)
Tim Robinson
A: 

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.

Stephan
+1  A: 
public class Comparer2<T, TKey> : IComparable<T>, IEqualityComparable<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(obj1));
    }
}

use it like this

ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
STO