使用LINQ GroupBy按引用对象分组,而不是按值对象分组

本文关键字:对象 GroupBy 使用 LINQ 引用 | 更新日期: 2023-09-27 18:00:12

我希望按记录列表中的多个对象分组,而不仅仅是多个值。

我在使用引用类型对象进行分组时遇到问题。我有一个包含Room、Type和DateTime的对象集合。Room、Type和DateTime都具有与其关联的属性。我已经在房间里添加了IEquatable接口和类型思维,这将足以与groupby配合。

var groups = collection
  .Where(g => g.Stage == InventoryStage.StageA)
  .GroupBy(g => new {g.BirthDate, g.Room, g.Type});

为了让这个代码工作,我必须调用这些对象的一个特定属性来分组。问题是,我需要存储在分组的"密钥"中的复杂对象,这样我就可以访问该组的特定信息

var groups = collection
  .Where(g => g.Stage == InventoryStage.StageA)
  .GroupBy(g => new {
     Birthday = g.BirthDate, 
     RoomName = g.Room.Name, 
     TypeName = g.Type.Name
   });

我最终不得不做^来让分组工作,然而分组失去了我想要的复杂对象。

使用LINQ GroupBy按引用对象分组,而不是按值对象分组

要完成此任务,您可以覆盖类的Equals()和GetHashCode()方法:

public class Room {
     public string Name;
     public string Foo;
     public override bool Equals(object obj)
     {
         Room other = obj as Room;
         if (other == null) return false;
         return this.Name == other.Name && this.Foo == other.Foo;
     }
     public override int GetHashCode()
     {
         return (Name.GetHashCode() ^ Foo.GetHashCode()).GetHashCode();
     }
}

看看这里更复杂的例子

  1. 您可以在包含这些属性的主类中重写Equals+GetHashCode,而不是在GroupBy中使用匿名类型
  2. 另一种方法是实现自定义IEqualityComparer<YourMainType>。您可以使用它的一个实例来重载GroupBy
  3. 您的RoomType可以覆盖Equals+GetHashCode或/并实现IEquatable<T>。实现IEquatable/IEquatable<>是不够的,因为GroupBy在开始将对象与Equals进行比较之前首先使用GetHashCode来确定哈希代码,所以这是初始过滤器

这里有一个房间类的例子:

public class Room:IEquatable<Room>
{
    public Room(string name)
    {
        Name = name;
    }
    public string Name { get; }
    /// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
    /// <returns>true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.</returns>
    /// <param name="other">An object to compare with this object.</param>
    public bool Equals(Room other)
    {
        return String.Equals(this.Name, other?.Name);
    }
    /// <summary>Determines whether the specified object is equal to the current object.</summary>
    /// <returns>true if the specified object  is equal to the current object; otherwise, false.</returns>
    /// <param name="obj">The object to compare with the current object. </param>
    public override bool Equals(object obj)
    {
        if(ReferenceEquals(this, obj))
            return true;
        Room other = obj as Room;
        return this.Equals(other);
    }
    /// <summary>Serves as the default hash function. </summary>
    /// <returns>A hash code for the current object.</returns>
    public override int GetHashCode()
    {
        return Name?.GetHashCode() ?? Int32.MinValue;
    }
}

现在,您甚至可以使用复杂类型作为匿名类型的属性。