Il se trouve que j'ai vu du code où ce type a passé une expression lambda à un ArrayList.Sort (IComparer ici) ou un IEnumerable.SequenceEqual (liste IEnumerable, IEqualityComparer ici) où un IComparer ou un IEqualityComparer était attendu.
Je ne peux pas être sûr si je l'ai vu ou si je rêve. Et je n'arrive pas à trouver une extension sur aucune de ces collections qui accepte un Func <> ou un délégué dans leurs signatures de méthode.
Existe-t-il une telle méthode de surcharge/extension? Ou, sinon, est-il possible de contourner comme ça et de passer un algorithme (lire délégué) où une interface à méthode unique est attendue?
Mise à jour Merci à tous. C'est ce que je pensais. Je devais rêver. Je sais comment écrire une conversion. Je ne savais simplement pas si j'avais vu quelque chose comme ça ou si je pensais juste l'avoir vu.
Encore une autre mise à jour Regardez, ici, j'ai trouvé une telle instance. Je ne rêvais pas après tout. Regardez ce que ce gars fait ici . Ce qui donne?
Et voici une autre mise à jour: D'accord, je comprends. Le gars utilise le Comparison<T>
surcharge. Agréable. Sympa, mais totalement enclin à vous induire en erreur. Bien cependant. Merci.
Je cherchais également sur le Web une solution, mais je n'en ai trouvé aucune satisfaisante. J'ai donc créé un EqualityComparerFactory générique:
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);
}
}
L'idée est que la méthode CreateComparer prend deux arguments: un délégué à GetHashCode (T) et un délégué à Equals (T, T)
Exemple:
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();
}
}
Je ne sais pas trop à quoi cela sert vraiment, car je pense que dans la plupart des cas dans la bibliothèque de base qui attend un IComparer, il y a une surcharge qui attend une comparaison ... mais juste pour mémoire:
dans .Net 4.5, ils ont ajouté une méthode pour obtenir un IComparer à partir d'une comparaison: Comparer.Create
afin que vous puissiez lui passer votre lambda et obtenir un IComparer.
Vous pouvez fournir un lambda pour une méthode Array.Sort, car elle nécessite une méthode qui accepte deux objets de type T et renvoie un entier. En tant que tel, vous pouvez fournir un lambda de la définition suivante (a, b) => a.CompareTo(b)
. Un exemple pour faire un tri décroissant d'un tableau entier:
int[] array = { 1, 8, 19, 4 };
// descending sort
Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
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));
}
}
l'utiliser comme ça
ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
Vous ne pouvez pas le transmettre directement, mais vous pouvez le faire en définissant une classe LambdaComparer
qui exclut un Func<T,T,int>
puis l'utilise dans son CompareTo
.
Ce n'est pas aussi concis mais vous pouvez le raccourcir grâce à des méthodes d'extension créatives sur Func
.
Je vote pour la théorie du rêve.
Vous ne pouvez pas passer une fonction où un objet est attendu: les dérivés de System.Delegate (qui sont les lambdas) n'implémentent pas ces interfaces.
Ce que vous avez probablement vu est une utilisation de la Converter<TInput, TOutput>
délégué, qui peut être modélisé par un lambda. Array.ConvertAll utilise une instance de ce délégué.
Ces méthodes n'ont pas de surcharge qui acceptent un délégué au lieu d'une interface, mais:
Enumerable.OrderBy
Enumerable.Select
avant d'appeler Enumerable.SequenceEqual
IEqualityComparer<T>
en terme de Func<T, T, bool>
Si vous avez besoin de cette fonction pour l'utiliser avec lambda et éventuellement deux types d'éléments différents:
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;
}
}