orderby - order object list c#
¿Cuál es la mejor práctica recomendada para usar IEqualityComparer<T>? (6)
Estoy buscando las mejores prácticas del mundo real, cómo otras personas podrían haber implementado soluciones con dominios complejos.
La lista usa mucho esta interfaz, por lo que puede decir a.Sustraer (b) u otras de estas agradables funciones.
Solo recuerde: si sus objetos no devuelven el mismo Hashcode, no se llama a los iguales.
Esto es lo que MSDN tiene que decir sobre IEqualityComparer (no genérico):
Esta interfaz permite la implementación de comparación de igualdad personalizada para colecciones. Es decir, puede crear su propia definición de igualdad y especificar que esta definición se use con un tipo de colección que acepte la interfaz
IEqualityComparer
. En .NET Framework, los constructores de los tipos de colecciónHashtable
,NameValueCollection
yOrderedDictionary
aceptan esta interfaz.Esta interfaz solo admite comparaciones de igualdad.
IComparer
interfaz deIComparer
proporciona la personalización de las comparaciones para ordenar y ordenar.
Parece que la versión genérica de esta interfaz realiza la misma función pero se utiliza para las colecciones de Dictionary<(Of <(TKey, TValue>)>)
.
En cuanto a las mejores prácticas sobre el uso de esta interfaz para sus propios fines. Diría que la mejor práctica sería usarla cuando se deriva o se implementa una clase que tiene una funcionalidad similar a las colecciones de framework .NET mencionadas anteriormente y donde se desea agregar la misma capacidad a sus propias colecciones. Esto garantizará que sea coherente con la forma en que .NET Framework usa la interfaz.
En otras palabras, respalde el uso de esta interfaz si está desarrollando una colección personalizada y desea permitir que sus consumidores controlen la igualdad que se utiliza en varios LINQ y métodos relacionados con la colección (por ejemplo, Ordenar).
Diría que el mejor uso sería cuando necesitas conectar diferentes reglas de igualdad para cierto algoritmo. De la misma manera que un algoritmo de clasificación podría aceptar un IComparer<T>
, un algoritmo de búsqueda podría aceptar un IEqualityComparer<T>
Cada vez que considere usar un IEqualityComparer<T>
, haga una pausa para pensar si la clase podría implementar IEquatable<T>
lugar. Si un Product
siempre debe ser comparado por ID, simplemente defínalo para que se equipare como tal para que pueda usar el comparador predeterminado.
Dicho esto, todavía hay algunas razones por las que es posible que desee un comparador personalizado:
- Si hay varias formas en que las instancias de una clase pueden considerarse iguales. El mejor ejemplo de esto es una cadena, para la cual el marco proporciona seis comparadores diferentes en
StringComparer
. - Si la clase está definida de tal manera que no puede definirla como
IEquatable<T>
. Esto incluiría las clases definidas por los demás y las clases generadas por el compilador (específicamente los tipos anónimos, que usan una comparación de propiedad por defecto).
Si decide que necesita un comparador, puede usar un comparador generalizado (vea la respuesta de DMenT), pero si necesita reutilizar esa lógica, debe encapsularla en una clase dedicada. Incluso podría declararlo heredando de la base genérica:
class ProductByIdComparer : GenericEqualityComparer<ShopByProduct>
{
public ProductByIdComparer()
: base((x, y) => x.ProductId == y.ProductId, z => z.ProductId)
{ }
}
En cuanto a su uso, debe aprovechar los comparadores cuando sea posible. Por ejemplo, en lugar de llamar a ToLower()
en cada cadena utilizada como clave de diccionario (la lógica se extenderá por su aplicación), debe declarar que el diccionario utiliza un StringComparer
distingue StringComparer
mayúsculas y minúsculas. Lo mismo aplica para los operadores LINQ que aceptan un comparador. Pero nuevamente, siempre considere si el comportamiento equitativo que debería ser intrínseco a la clase en lugar de ser definido externamente.
Hice lo siguiente, no estoy seguro si es la mejor práctica del mundo real, pero funcionó bien para mí. :)
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, Boolean> _comparer;
private Func<T, int> _hashCodeEvaluator;
public GenericEqualityComparer(Func<T, T, Boolean> comparer)
{
_comparer = comparer;
}
public GenericEqualityComparer(Func<T, T, Boolean> comparer, Func<T, int> hashCodeEvaluator)
{
_comparer = comparer;
_hashCodeEvaluator = hashCodeEvaluator;
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
if(obj == null) {
throw new ArgumentNullException("obj");
}
if(_hashCodeEvaluator == null) {
return 0;
}
return _hashCodeEvaluator(obj);
}
#endregion
}
Entonces puedes usarlo en tus colecciones.
var comparer = new GenericEqualityComparer<ShopByProduct>((x, y) => x.ProductId == y.ProductId);
var current = SelectAll().Where(p => p.ShopByGroup == group).ToList();
var toDelete = current.Except(products, comparer);
var toAdd = products.Except(current, comparer);
Si necesita admitir la funcionalidad GetHashCode () personalizada, utilice el constructor alternativo para proporcionar una lambda para realizar el cálculo alternativo:
var comparer = new GenericEqualityComparer<ShopByProduct>(
(x, y) => { return x.ProductId == y.ProductId; },
(x) => { return x.Product.GetHashCode()}
);
Espero que esto ayude. =)
Consulte esta publicación para conocer las (mejores) alternativas: ajustar un delegado en un IEqualityComparer
Desplácese hacia abajo hasta la parte en KeyEqualityComparer y especialmente la parte sobre la importancia de GetHashCode . Hay una discusión completa sobre por qué obj.GetHashCode();
(como lo sugiere la publicación de DMenT) es incorrecto y debería simplemente devolver 0 en su lugar.