LINQ Select Distinct con tipos anónimos

Entonces tengo una colección de objetos. El tipo exacto no es importante. De ella quiero extraer todos los pares únicos de un par de propiedades particulares, así:

myObjectCollection.Select(item=>new { Alpha = item.propOne, Bravo = item.propTwo } ).Distinct(); 

Entonces mi pregunta es: Distintará en este caso usar el objeto predeterminado igual (que será inútil para mí, ya que cada objeto es nuevo) o se le puede decir que haga un igual diferente (en este caso, valores iguales de Alfa y Bravo) => instancias iguales)? ¿Hay alguna manera de lograr ese resultado, si esto no lo hace?

Lea el excelente post de K. Scott Allen aquí:

E igual para todos … Tipos anónimos

La respuesta corta (y cito):

Resulta que el comstackdor de C # anula Equals y GetHashCode para los tipos anónimos. La implementación de los dos métodos reemplazados utiliza todas las propiedades públicas del tipo para calcular el código hash de un objeto y probar la igualdad. Si dos objetos del mismo tipo anónimo tienen todos los mismos valores para sus propiedades, los objetos son iguales.

Por lo tanto, es totalmente seguro utilizar el método Distinct () en una consulta que devuelve tipos anónimos.

 public class DelegateComparer : IEqualityComparer { private Func _equals; private Func _hashCode; public DelegateComparer(Func equals, Func hashCode) { _equals= equals; _hashCode = hashCode; } public bool Equals(T x, T y) { return _equals(x, y); } public int GetHashCode(T obj) { if(_hashCode!=null) return _hashCode(obj); return obj.GetHashCode(); } } public static class Extensions { public static IEnumerable Distinct(this IEnumerable items, Func equals, Func hashCode) { return items.Distinct(new DelegateComparer(equals, hashCode)); } public static IEnumerable Distinct(this IEnumerable items, Func equals) { return items.Distinct(new DelegateComparer(equals,null)); } } var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList(); 

Perdón por el formateo desordenado anterior

Es interesante que funcione en C # pero no en VB

Devuelve las 26 letras:

 var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"; MyBet.ToCharArray() .Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()}) .Distinct() .Dump(); 

Devuelve 52 …

 Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" MyBet.ToCharArray() _ .Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _ .Distinct() _ .Dump() 

Ejecuté una pequeña prueba y descubrí que si las propiedades son tipos de valores, parece funcionar bien. Si no son tipos de valor, entonces el tipo necesita proporcionar sus propias implementaciones Equal y GetHashCode para que funcione. Cuerdas, creo, funcionarían.

Puede crear su propio método de Extensión Distinct que tome la expresión lambda. Aquí hay un ejemplo

Crea una clase que se deriva de la interfaz IEqualityComparer

 public class DelegateComparer : IEqualityComparer { private Func _equals; private Func _hashCode; public DelegateComparer(Func equals, Func hashCode) { _equals= equals; _hashCode = hashCode; } public bool Equals(T x, T y) { return _equals(x, y); } public int GetHashCode(T obj) { if(_hashCode!=null) return _hashCode(obj); return obj.GetHashCode(); } } 

Luego crea tu método de Extensión Distinct

 public static class Extensions { public static IEnumerable Distinct(this IEnumerable items, Func equals, Func hashCode) { return items.Distinct(new DelegateComparer(equals, hashCode)); } public static IEnumerable Distinct(this IEnumerable items, Func equals) { return items.Distinct(new DelegateComparer(equals,null)); } } 

y puedes usar este método para encontrar elementos distintos

 var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList(); 

Si Alpha y Bravo heredan de una clase común, usted podrá dictar la verificación de igualdad en la clase padre implementando IEquatable .

Por ejemplo:

 public class CommonClass : IEquatable { // needed for Distinct() public override int GetHashCode() { return base.GetHashCode(); } public bool Equals(CommonClass other) { if (other == null) return false; return [equality test]; } } 

Hola, tengo el mismo problema y encontré una solución. Debe implementar la interfaz IEquatable o simplemente anular los métodos (Equals & GetHashCode). Pero este no es el truco, el truco está en el Método GetHashCode. No debe devolver el código hash del objeto de su clase, pero debe devolver el hash de la propiedad que desea comparar así.

 public override bool Equals(object obj) { Person p = obj as Person; if ( obj == null ) return false; if ( object.ReferenceEquals( p , this ) ) return true; if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian ) return true; return false; //return base.Equals( obj ); } public override int GetHashCode() { return Name.GetHashCode(); } 

Como ves obtuve una clase llamada Persona obtuve 3 propiedades (Nombre, Edad, IsEgyptian “Porque soy”) En el GetHashCode, devolví el hash de la propiedad Name y no el objeto Person.

Pruébalo y funcionará ISA. Gracias, Modather Sadik

Para que funcione en VB.NET, debe especificar la palabra Key clave antes de cada propiedad en el tipo anónimo, como esta:

 myObjectCollection.Select(Function(item) New With { Key .Alpha = item.propOne, Key .Bravo = item.propTwo }).Distinct() 

Estaba luchando con esto, pensé que VB.NET no era compatible con este tipo de características, pero en realidad lo hace.