Pass a lambda expression in place of IComparer or IEqualityComparer or any single-method interface?
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.
Solution 1:
I'm not much sure what useful it really is, as I think for most cases in the Base Library expecting an IComparer there's an overload that expects a Comparison... but just for the record:
in .Net 4.5 they've added a method to obtain an IComparer from a Comparison: Comparer.Create
so you can pass your lambda to it and obtain an IComparer.
Solution 2:
I was also googling the web for a solution, but i didn't found any satisfying one. So i've created a generic 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 to GetHashCode(T) and a delegate to 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();
}
}