Using LINQ GroupBy to group by reference objects instead of value objects

匆匆过客 提交于 2020-08-26 09:56:13

问题


I want to GroupBy multiple objects in a list of records and not simply multiple values.

I am having trouble getting grouping to work with reference type objects. I have a collection of objects that contain Room, Type, and DateTime. Room, Type, and DateTime all have properties associated with them. I've already added the IEquateable interface to the room and the type thinking that would be enough to with with group by.

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

In order for this code to work i have to call our a specific property on these objects to group by. The problem with that is that i need the complex objects stored in the "Key" of the grouping so that i can access that groups specific information

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

I end up having to do ^ to get the grouping to work, however the groups lose the complicated object i wanted.


回答1:


To accomplish this task, you can override Equals() and GetHashCode() methods for your classes:

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

Take a look here for more complicated example




回答2:


  1. Your could override Equals + GetHashCode in the main class that holds those properties instead of using the anonymous type in the GroupBy.
  2. Another approach would be to implement a custom IEqualityComparer<YourMainType>. You can use an instance of it for the overload of GroupBy.
  3. your Room and Type could override Equals + GetHashCode or/and implement IEquatable<T>. Implementing IEquatable/IEquatable<> isn't enough since GroupBy first uses GetHashCode to determine the hashcode before it starts to compare objects with Equals, so that's the initial filter.

Here's an example for the Room class:

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

Now you can even use complex types as a property of an anonymous type.



来源:https://stackoverflow.com/questions/38761686/using-linq-groupby-to-group-by-reference-objects-instead-of-value-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!