linq - sintaxis - ¿Pasa una expresión lambda en lugar de IComparer o IEqualityComparer o cualquier interfaz de método único?
lambda sintaxis (8)
He visto un código donde este tipo pasó una expresión lambda a ArrayList.Sort (IComparer aquí) o IEnumerable.SequenceEqual (lista IEnumerable, IEqualityComparer aquí) donde se esperaba un IComparer o un IEqualityComparer.
No puedo estar seguro si lo vi, o solo estoy soñando. Y no puedo encontrar una extensión en ninguna de estas colecciones que acepte un Func <> o un delegado en sus firmas de métodos.
¿Hay tal método de sobrecarga / extensión? O, si no es así, ¿es posible hacer un rodeo así y pasar un algoritmo (leer delegado) donde se espera una interfaz de un solo método?
Actualización Gracias, a todos. Es lo que pensaba. Debo haber estado soñando. Sé cómo escribir una conversión. Simplemente no estaba seguro de si había visto algo así o simplemente pensé que lo había visto.
Otra actualización Mira, aquí encontré una de esas instancias. No estaba soñando después de todo. Mira lo que este tipo está haciendo aquí . ¿Lo que da?
Y aquí hay otra actualización: Ok, lo entiendo. El tipo usa la sobrecarga Comparison<T>
. Bonito. Agradable, pero totalmente propenso a engañarte. Bien, sin embargo. Gracias.
En caso de que necesite esta función para usarla con lambda y posiblemente dos tipos diferentes de elementos:
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;
}
}
Estos métodos no tienen sobrecargas que aceptan un delegado en lugar de una interfaz, pero:
- Normalmente puede devolver una clave de clasificación más simple a través del delegado que pasa a
Enumerable.OrderBy
- Del mismo modo, puede llamar a
Enumerable.Select
antes de llamar aEnumerable.SequenceEqual
- Debería ser sencillo escribir un contenedor que implemente
IEqualityComparer<T>
en términos deFunc<T, T, bool>
- F # te permite implementar este tipo de interfaz en términos de lambda :)
No estoy muy seguro de lo útil que realmente es, ya que creo que para la mayoría de los casos en la Biblioteca de Base esperando un IComparer hay una sobrecarga que espera una Comparación ... pero solo para el registro:
en .Net 4.5 han agregado un método para obtener un IComparer de una comparación: Comparer.Create
para que pueda pasarle su lambda y obtener un IComparer.
No puede pasarlo directamente, pero puede hacerlo definiendo una clase LambdaComparer
que exceptúe un Func<T,T,int>
y luego lo use en CompareTo
.
No es tan conciso, pero podría hacerlo más corto a través de algunos métodos de extensión creativos en Func
.
Puede proporcionar un lambda para un método Array.Sort, ya que requiere un método que acepte dos objetos de tipo T y devuelva un entero. Como tal, podría proporcionar una lambda de la siguiente definición (a, b) => a.CompareTo(b)
. Un ejemplo para hacer un tipo descendente de una matriz de enteros:
int[] array = { 1, 8, 19, 4 };
// descending sort
Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
También buscaba en la web una solución, pero no encontré ninguna que me satisficiera. Así que creé una EqualityComparerFactory genérica:
public static class EqualityComparerFactory<T>
{
private class MyComparer : IEqualityComparer<T>
{
private readonly Func<T, int> _getHashCodeFunc;
private readonly Func<T, T, bool> _equalsFunc;
public MyComparer(Func<T, int> getHashCodeFunc, Func<T, T, bool> equalsFunc)
{
_getHashCodeFunc = getHashCodeFunc;
_equalsFunc = equalsFunc;
}
public bool Equals(T x, T y)
{
return _equalsFunc(x, y);
}
public int GetHashCode(T obj)
{
return _getHashCodeFunc(obj);
}
}
public static IEqualityComparer<T> CreateComparer(Func<T, int> getHashCodeFunc, Func<T, T, bool> equalsFunc)
{
if (getHashCodeFunc == null)
throw new ArgumentNullException("getHashCodeFunc");
if (equalsFunc == null)
throw new ArgumentNullException("equalsFunc");
return new MyComparer(getHashCodeFunc, equalsFunc);
}
}
La idea es que el método CreateComparer toma dos argumentos: un delegado a GetHashCode (T) y un delegado a Equals (T, T)
Ejemplo:
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<Person>.CreateComparer(a => a.Id.GetHashCode(), (a, b) => a.Id==b.Id);
var intersection = list1.Intersect(list2, comparer).ToList();
}
}
Yo voto por la teoría de los sueños.
No se puede pasar una función donde se espera un objeto: los derivados de System.Delegate (que es lo que lambdas son) no implementan esas interfaces.
Lo que probablemente vio es un uso del delegado Converter<TInput, TOutput>
, que puede ser modelado por un lambda. Array.ConvertAll usa una instancia de este delegado.
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));
}
}
Úselo así
ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));