在linq分组查询中使用严格类型

本文关键字:类型 linq 查询 | 更新日期: 2023-09-27 17:54:39

我正在使用linq查询一个集合,并使用以下代码通过datetime属性分组我的数据:

var querty = from post in ds.Appointments
             group post by new  
             { 
                 Year = post.DateOfVisit.Year, 
                 Month = post.DateOfVisit.Month
             };
当使用匿名类型时,一切都很好。但是如果我定义自己的类
class YearMonth
{
    public int Year;
    public string Month;
    public YearMonth(int year, int month)
    {
        Year = year;
        Month = month;
    }
    public override string ToString()
    {
        return string.Format("{0}-{1}",Year,Month);
    }
}

并相应地修改我的查询

var querty = from post in ds.Appointments
             group post by new YearMonth(post.DateOfVisit.Year, 
                                         post.DateOfVisit.Month);

则分组不起作用,我得到的是对象的普通列表。为什么?

在linq分组查询中使用严格类型

正如schglurps已经说过的,您必须覆盖GetHashCodeEquals,因为GroupBy方法(和其他方法)依赖于它们。


GroupBy方法根据分组对象的哈希码(和相等性)创建最终组。

因此,对于序列中的每个项,它检查是否已经存在具有相同哈希码的组,然后检查该项是否等于该组的键。如果没有匹配的组,它会创建一个新的组。

在你的例子中,因为你没有覆盖GetHashCode, YearMonth的每个实例将有一个不同的哈希码(冲突除外),所以每个项目将导致一个新的组被创建。

请看一下相关的参考资料。


下面是一个示例实现:

public class YearMonth : IEquatable<YearMonth>
{
    public readonly int Year;
    public readonly int Month;
    public YearMonth(int year, int month)
    {
        Year = year;
        Month = month;
    }
    public override string ToString()
    {
        return string.Format("{0}-{1}", Year, Month);
    }
    public bool Equals(YearMonth other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Month == other.Month && Year == other.Year;
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((YearMonth)obj);
    }
    public override int GetHashCode()
    {
        unchecked
        {
            return (Month * 397) ^ Year;
        }
    }
    public static bool operator ==(YearMonth left, YearMonth right)
    {
        return Equals(left, right);
    }
    public static bool operator !=(YearMonth left, YearMonth right)
    {
        return !Equals(left, right);
    }
}