2014-11-06 77 views
3

我想将一个grouppedList传递给几个函数,但是当尝试使用一个类而不是一个匿名类型进行分组时,我得到了奇怪的结果。在以下示例中,匿名类型按预期返回2个结果,但具体类返回5,就好像它没有分组一样。Linq to Sql Group按类

问题:是否有可能通过具体的类来执行Linq to Sql Group?

public class person 
{ 
    public string Name; 
    public string State; 
} 

public class personGroup 
{ 
    public string State; 
    public personGroup(string personState) 
    { 
     State = personState; 
    } 
} 


void Main() 
{ 
    var people = new List<person>(); 
    people.Add(new person {Name = "Bob", State = "Tx"}); 
    people.Add(new person {Name = "Bill", State = "Tx"}); 
    people.Add(new person {Name = "Tracy", State = "Tx"}); 
    people.Add(new person {Name = "Steve", State = "Md"}); 
    people.Add(new person {Name = "Kelly", State = "Md"}); 

    var groupedPeople = people.GroupBy (p => p.State); 
    groupedPeople.Count().Dump();//Returns 2 

    var morePeople = people.GroupBy (p => new personGroup(p.State)); 
    morePeople.Count().Dump();//Returns 5 
} 
+1

在这个问题中没有“SQL”。我想你只是说LINQ。 – 2014-11-06 21:26:43

回答

4

GroupBy方法使用EqualityComparer<T>.Default来比较没有提供自定义IEqualityComparer(您没有提供一个)有问题的项目。这将基于T类型的IEquatable<T>的实现,如果有的话,如果没有,那么它将简单地使用该类型的object.Equalsobject.GetHashCode

您的自定义类型不提供任何实现,它完全依赖于object中定义的实现,对于引用类型,该实现基于对象的引用。您创建的每个personGroup对象都有不同的参考,因此不同。

匿名类型不使用默认的平等behvaior;它们覆盖EqualsGetHashCode的定义,以取决于它们表示的每个属性的标识。

如果你想使用自己定制的类型进行分组,并默认平等语义不是你想要的,你要么需要提供自定义IEqualityComparer实现,或覆盖EqualsGetHashCode在类型题。

+0

我看到如何覆盖Equals,但是我将使用GetHashCode,它是否需要对每个组都是唯一的,或者我可以只返回1.GetHashCode – SumGuy 2014-11-06 21:36:37

+0

@SumGuy阅读该方法的文档,看看实施它时需要遵循的准则。在这个特定情况下,你可能只需要返回该组正在包装的'State'的哈希码,但在更复杂的情况下,你可能需要做更多的事情来生成一个好的哈希。 – Servy 2014-11-06 21:37:57

3

在第二组中,有5组,因为密钥总是不同的。 personGroups通过引用进行比较,并且所有对象都有不同的参考。您需要覆盖personGroup类中的EqualsGetHashCode方法,以根据状态比较您的实例,或实施IEqualityComparer<personGroup>并将其传递到GroupBy

+0

你如何将它传递给GroupBy? – SumGuy 2014-11-06 21:36:56

+1

'people.GroupBy(p => new personGroup(p.State),new MyEqualityComparer())' – 2014-11-06 21:37:33

0

由于Servy上面已经说过,您需要创建一个PersonGroup类型的自定义IEqualityComparer以便您的示例正常工作。

我已经遇到过很多我自己,所以我创建了一个通用的EqualityComparer,它可以与任何模型(使用属性)一起工作!

/// <summary> 
/// Given two models, return TRUE if all the properties are equal, 
/// else return FALSE 
/// </summary> 
/// <typeparam name="TModel">Type of the models being compared</typeparam> 
public class PropertyEqualityComparer<TModel> : IEqualityComparer<TModel> 
{ 
    public bool Equals(TModel x, TModel y) 
    { 
     PropertyInfo[] props = typeof(TModel).GetProperties(); 
     foreach (PropertyInfo prop in props) 
     { 
      if (!Object.Equals(prop.GetValue(x), prop.GetValue(y))) 
      { 
       return false; 
      }  
     } 
     return true; 
    } 

    public int GetHashCode(TModel obj) 
    { 
     int hash = 1; 
     PropertyInfo[] props = typeof(TModel).GetProperties(); 
     foreach (PropertyInfo prop in props) 
     { 
      hash ^= prop.GetValue(obj).GetHashCode(); 
     } 
     return hash; 
    } 
} 

有了这门课,你将能够做到你所想。

// NOTE: Use properties instead of public variables 
public class Person 
{ 
    public string Name { get; set; } 
    public string State { get; set; } 
} 

// NOTE: Use properties instead of public variables 
public class personGroup 
{ 
    public string State { get; set; } 
} 

void Main() 
{ 
    var people = new List<Person>(); 
    people.Add(new Person{Name = "Bob", State = "Tx"}); 
    people.Add(new Person{Name = "Bill", State = "Tx"}); 

    var morePeople = people.GroupBy(p => new PersonGroup{State = p.State}, new PropertyEqualityComparer<PersonGroup>()); 
    morePeople.Count(); 
}