Pass lambda expression instead of IComparer or IEqualityComparer or any interface of one method?

I happened to see some code in which this guy passed the lambda expression to ArrayList.Sort (here IComparer) or IEnumerable.SequenceEqual (list IEnumerable, IEqualityComparer here), where IComparer or IEqualityComparer was expected.

I can’t be sure if I saw it, or I’m just dreaming. And I cannot find an extension in any of these collections that accepts Func <> or a delegate in its method signatures.

Is there such an overload / extension method? Or, if not, is it possible to cheat this and pass an algorithm (read delegate), where an interface with one method is expected?

Update Thank you all. This is what I thought. I must have been dreaming. I know how to write a conversion. I was just not sure if I saw something like this or just thought I saw it.

Another update. Listen, here I found one such instance. In the end, I did not dream. See what this guy is doing here . What gives?

And here is another update: Ok, I understand. The guy uses the Comparison<T> overload. Nice. Pleasant, but quite apt to mislead you. Nice, however. Thank.

+56
linq extension-methods ienumerable icomparer iequalitycomparer
Jul 06 '10 at 20:13
source share
8 answers

I am not very sure that this is really the way I think, in most cases in the Base Library it is expected that IComparer there will be an overload awaiting comparison ... but just for the record:

in .Net 4.5 they added a method to get IComparer from comparison: Comparer.Create

so that you can pass your lambda and get IComparer.

+22
Jun 11 2018-12-12T00:
source share

I also searched the Internet for a solution, but did not find any satisfactory. So, I created a universal 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); } } 

The idea is that the CreateComparer method takes two arguments: a delegate for GetHashCode (T) and a delegate for Equals (T, T)

Example:

 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(); } } 
+22
Sep 24 '13 at 14:18
source share

You can provide lambda for the Array.Sort method, since it requires a method that takes two objects of type T and returns an integer. So you can provide lambda with the following definition (a, b) => a.CompareTo(b) . An example of creating a downward sort of an integer array:

 int[] array = { 1, 8, 19, 4 }; // descending sort Array.Sort(array, (a, b) => -1 * a.CompareTo(b)); 
+11
Jul 06 '10 at 20:18
source share
 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 that

 ArrayList.Sort(new Comparer2<Product, string>(p => p.Name)); 
+5
Jul 6 '10 at 20:22
source share

You cannot pass it directly, but you can do it by specifying the LambdaComparer class that excludes Func<T,T,int> and then uses it in CompareTo .

It's not entirely concise, but you can shorten it with some func extension methods.

+4
Jul 06 '10 at 20:21
source share

I vote for the theory of dreams.

You cannot pass the function in which the object is expected: derivatives of System.Delegate (this is what lambdas is) do not implement these interfaces.

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

+3
Jul 6 '10 at 20:17
source share

These methods do not have overloads that accept a delegate instead of an interface, but:

  • You can usually return a simpler sort key through the delegate that you pass to Enumerable.OrderBy
  • Similarly, you can call Enumerable.Select before calling Enumerable.SequenceEqual
  • It should be easy to write a shell that implements IEqualityComparer<T> in terms of Func<T, T, bool>
  • F # allows you to implement this kind of interface in terms of lambda :)
+3
Jul 06 '10 at 20:19
source share

If you need this function for use with lambda and possibly with two different types of elements:

 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; } } 
+1
Jul 26 '16 at 16:13
source share



All Articles