Tenía la misma pregunta. Cuando ejecuté el ejemplo de LBushkin, me sorprendí al ver que obtuve una respuesta diferente. A pesar de que esa respuesta tiene 8 votos ascendentes, está mal. Después de un montón de 'reflector', aquí está mi opinión sobre las cosas.
Ciertos contenedores (matrices, tuplas, tipos anónimos) admiten IStructuralComparable e IStructuralEquatable.
IStructuralComparable admite una clasificación profunda y predeterminada.
IStructuralEquatable admite hash profundo y predeterminado.
{Tenga en cuenta que EqualityComparer<T>
apoya poco profundo (nivel de sólo el 1 contenedor), hash por defecto.}
Por lo que yo veo esto sólo se expone a través de la clase StructuralComparisons. La única manera que puedo imaginar para hacer de este útil es hacer una clase StructuralEqualityComparer<T>
ayudante de la siguiente manera:
public class StructuralEqualityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(x,y);
}
public int GetHashCode(T obj)
{
return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
private static StructuralEqualityComparer<T> defaultComparer;
public static StructuralEqualityComparer<T> Default
{
get
{
StructuralEqualityComparer<T> comparer = defaultComparer;
if (comparer == null)
{
comparer = new StructuralEqualityComparer<T>();
defaultComparer = comparer;
}
return comparer;
}
}
}
Ahora podemos hacer un HashSet con los objetos que tienen contenedores dentro de los contenedores dentro de los contenedores.
var item1 = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
var item1Clone = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
var item2 = Tuple.Create(1, new int[][] { new int[] { 1, 3 }, new int[] { 3 } });
var set = new HashSet<Tuple<int, int[][]>>(StructuralEqualityComparer<Tuple<int, int[][]>>.Default);
Console.WriteLine(set.Add(item1)); //true
Console.WriteLine(set.Add(item1Clone)); //false
Console.WriteLine(set.Add(item2)); //true
También podemos hacer que nuestro propio contenedor funcione bien con estos otros contenedores mediante la implementación de estas interfaces.
public class StructuralLinkedList<T> : LinkedList<T>, IStructuralEquatable
{
public bool Equals(object other, IEqualityComparer comparer)
{
if (other == null)
return false;
StructuralLinkedList<T> otherList = other as StructuralLinkedList<T>;
if (otherList == null)
return false;
using(var thisItem = this.GetEnumerator())
using (var otherItem = otherList.GetEnumerator())
{
while (true)
{
bool thisDone = !thisItem.MoveNext();
bool otherDone = !otherItem.MoveNext();
if (thisDone && otherDone)
break;
if (thisDone || otherDone)
return false;
if (!comparer.Equals(thisItem.Current, otherItem.Current))
return false;
}
}
return true;
}
public int GetHashCode(IEqualityComparer comparer)
{
var result = 0;
foreach (var item in this)
result = result * 31 + comparer.GetHashCode(item);
return result;
}
public void Add(T item)
{
this.AddLast(item);
}
}
Ahora podemos hacer un HashSet con elementos que tienen contenedores dentro de contenedores personalizados dentro de contenedores.
var item1 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
var item1Clone = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
var item2 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 3 }, new int[] { 3 } });
var set = new HashSet<Tuple<int, StructuralLinkedList<int[]>>>(StructuralEqualityComparer<Tuple<int, StructuralLinkedList<int[]>>>.Default);
Console.WriteLine(set.Add(item1)); //true
Console.WriteLine(set.Add(item1Clone)); //false
Console.WriteLine(set.Add(item2)); //true
Por qué no puede simplemente especificar un 'sí mismo que IEqualityComparer' ¿Haz esto? ¿Qué agrega la interfaz 'IStructuralEquatable' a esto? – thecoop
@thecoop: hay dos razones. Primero, no todos los tipos implementan una sobrecarga de 'Equals' que acepta un' IEqualityComparer' - Array es un ejemplo, IIRC. En segundo lugar, es agradable proporcionar un comparador de igualdad, pero ¿qué ocurre si desea expresar el hecho de que cierto método requiere dos objetos que puedan compararse estructuralmente? Ser capaz de especificar 'IStructuralEquatable' /' IStructuralComparable' en tales casos es realmente útil. También sería inconveniente pasar un 'TupleComparer' o' ArrayComparer' donde quiera aplicar este tipo de comparación. Los dos enfoques no son mutuamente excluyentes. – LBushkin
¿Cómo se relacionan estos comparadores con cosas como Dictionary y otras colecciones? Sé que el diccionario parece manejar las estructuras con sensatez, aunque lentamente en .Net 2.0; ¿.Net 4.0 (o 3.x para el caso) permite que las matrices se almacenen convenientemente en Diccionario (usando los contenidos de la matriz como la clave)? – supercat