Linq - 使用 GroupBy 与自定义类型 vs 匿名类型

5

我有一个对象列表,想要用Linq进行分组。这些对象的类型是GroupRating。我想按照它们的“Params”属性进行分组。

public class GroupRating
{
    public long Id { get; set; }
    public Parameters Params { get; set; }
}

public class Parameters
{
    public int CarrierId { get; set; }
    public int CustomerId { get; set; }
}

事实上,这是有效的: (即,我只得到一个包含所有id的组)
        var myList = new List<GroupRating>();
        ... blahblah code...

        var groupedList = myList.GroupBy(i => new {
             CarrierId = i.Params.CarrierId,
             CustomerId = i.Params.CustomerId
         }, i => i.Id).ToArray();

但这并不起作用: (即我得到与Id数量相同的组)
        var myList = new List<GroupRating>();
        ... blahblah code...

        var groupedList = myList.GroupBy(i => new Params {
             CarrierId = i.Params.CarrierId,
             CustomerId = i.Params.CustomerId
         }, i => i.Id).ToArray();

有任何想法为什么呢?
谢谢。
2个回答

10
你的类必须正确地重写Equals(object)和GetHashCode()方法。否则,即使两个看起来相同的new Params { ... },它们也不会被认为是“相等”的。
匿名类型会自动重写这两个方法。
你也可以使用struct而不是class,因为struct使用了System.ValueType中存在的Equals(object)和GetHashCode()的重写方法。如果选择struct,请考虑将类型设置为不可变,即将属性设置为只读(或使用private set;)。
后续补充:从2020年开始(C# 9),如果你想让自己更轻松,应该使用record类型。然后,C#编译器会自动生成与每种情况下你的确切需求相匹配的Equals(object)GetHashCode()实现(无需反射)。自2021年起(C# 10),如果你想要一个值类型记录,也可以选择record struct

1
+1,但是... A. 结构体中默认的EqualsGetHashCode方法远非最佳选择,因此即使使用结构体,我建议重写它们;B. 即使在类中,如果可能的话,始终值得考虑不可变性。 - Jon Hanna
@JonHanna 我同意这一点(不过,在结构体仅有两个非静态字段且都是 int 类型的情况下,ValueType 的实现会有多低效呢?)。 - Jeppe Stig Nielsen
关于效率,这可能是最低劣的了,但没有彻底疯狂。这反映得很好,而我十分支持反射以获得乐趣和利润,但我仍然建议在任何你知道永远不会比较相等的结构体私有类型上覆盖等式。 - Jon Hanna
谢谢,我明白了! - Damien7792

0

你必须使用类或匿名类型。Params 不是类,但你可以使用它。

试试这个:

  var groupedList = myList.GroupBy(i => new Parameters {
                 CarrierId = i.CarrierId,
                 CustomerId = i.CustomerId
             }, i => i.Id).ToArray();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接