2009-02-12 117 views
133

所以我有一个对象的集合。确切的类型并不重要。从中我想提取一对特定属性的所有唯一对,正是如此:LINQ选择不同于匿名类型

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

所以我的问题是:不同之处在这种情况下使用默认的对象等于(这将是对我没用,因为每个对象都是新的),还是可以告诉做不同的等号(在这种情况下,Alpha和Bravo的值相等=>等于实例)?有没有办法实现这个结果,如果这不行的话?

+0

这是LINQ到对象或LINQ到SQL?如果只是物体,你可能不太好。但是,如果是L2S,那么它可能会工作,因为DISTINCT会传递到SQL语句中。 – 2009-02-12 21:54:27

回答

163

必须通过K.斯科特·艾伦的出色的帖子在这里读:

And Equality for All ... Anonymous Types

简短的回答(我引述):

原来的C#编译器覆盖 的Equals和GetHashCode为匿名 类型。两个 重写方法的实现使用该类型上的所有公共 属性来计算对象的哈希码并测试 的等式。如果同一个 匿名类型的两个对象的属性值都相同,则 对象相等。

因此,在返回匿名类型的查询中使用Distinct()方法是完全安全的。

+1

这只是真实的,我认为,如果属性本身是价值类型或实现价值平等 - 请参阅我的答案。 – tvanfosson 2009-02-12 22:09:38

+0

是的,因为它在每个属性上使用GetHashCode,所以它只会在每个属性都有其自己的唯一实现时才起作用。我认为大多数使用情况只会涉及简单的类型作为属性,所以它通常是安全的。 – 2009-02-12 22:23:11

+3

它意味着两个匿名类型的平等取决于成员的平等,这对我来说很好,因为如果必须的话,成员可以定义在某个地方并且可以覆盖相等。我只是不想为此创建一个类来覆盖equals。 – GWLlosa 2009-02-12 22:24:24

0

如果AlphaBravo都从一个普通的类继承,您将能够通过执行IEquatable<T>来指定父类中的相等性检查。

例如:

public class CommonClass : IEquatable<CommonClass> 
{ 
    // needed for Distinct() 
    public override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 

    public bool Equals(CommonClass other) 
    { 
     if (other == null) return false; 
     return [equality test]; 
    } 
} 
3

我跑了一个小测试,发现如果属性值的类型,它似乎工作正常。如果它们不是值类型,那么类型需要提供它自己的Equals和GetHashCode实现。我想,弦乐会起作用。

2

您可以创建您自己的Distinct扩展方法,它需要lambda表达式。这里有一个例子

创建从的IEqualityComparer接口得出的

public class DelegateComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, bool> _equals; 
    private Func<T, int> _hashCode; 
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> 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<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals, Func<T,int> hashCode) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals, hashCode));  
    } 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals) 
    { 
     return items.Distinct(new DelegateComparer<T>(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(); 
14
public class DelegateComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, bool> _equals; 
    private Func<T, int> _hashCode; 
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> 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<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals, Func<T,int> hashCode) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals, hashCode));  
    } 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals) 
    { 
     return items.Distinct(new DelegateComparer<T>(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(); 

对不起,我搞砸了格式化早些时候

4

有趣的是它工作在C#中,但不是在VB

返回的26个字母:

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

返回52 ...

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

嘿,我有同样的问题,我找到了解决办法。 您必须实现IEquatable接口或简单地覆盖 (等于& GetHashCode)方法。但这不是诀窍,GetHashCode方法中的诀窍。你不应该返回你类的对象的哈希码,但你应该返回你想要比较的属性的哈希值。

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(); 
    } 

正如你看到的我叫了一个人得了类3点的属性(姓名,年龄,IsEgyptian“因为我”)在我的GetHashCode返回的名称属性不是Person对象的哈希值。

试试看,它会工作ISA。 谢谢, Modather Sadik