.NET如何确定LINQ中的相同对象“选择新”?

时间:2011-07-31 09:23:42

标签: .net linq equals equality

以下是示例:

class A
{ 
    public long ParentId; 
    public string ParentName; 
    public string Name; 
}

// list is IEnumerable<A>
var selected = list
    .Select(x => new {
        parent = new {
            id = x.ParentId,
            name = x.ParentName
        }
        name = x.Name
    });
var grouped = selected.GroupBy(x => x.parent);

因此,在分组成功完成后,我得出结论,如果两个实体都具有相同的parentParentId,则不会为两个不同的实体创建ParentName。换句话说,如果list[i].ParentId == list[j].ParentIdlist[i].ParentName == list[j].ParentName,则选择selected[i].parent == selected[j].parent后 我对吗?我想,new通过源集合在每次迭代中创建新对象。 .NET如何做到这一点?

5 个答案:

答案 0 :(得分:4)

这是Equals如何受到重视的问题。对于匿名类,当且仅当属性匹配时才返回true,并且对于每个属性都是相等的。

比较这两个:

Console.WriteLine(new { A = 1, B = "a" } == new { A = 1, B = "a" });  //false
Console.WriteLine(new { A = 1, B = "a" }.Equals(new { A = 1, B = "a" }));  //true

答案 1 :(得分:1)

GroupBy使用标准哈希+等于方法(与HashtableDictionary<,>等相同),也就是说:

  • GetHashCode定义明确的不相等(当不同时)和可能的相等(当相同时)
  • 等于(IEquatable<T>.Equalsobject.Equals)定义相等

您的投影组比较ParentName,因此在比较项目时使用。由于string定义明确GetHashCode / Equals,因此效果很好。

回答你的最后一个问题:

  

我想,新的通过源集合在每次迭代中创建新对象。

严格地说,是的。但它只列举一次,所以这不是问题。即使它没有,匿名类型new {...} 本身也有基于组件成员的相等定义。

答案 2 :(得分:0)

它不会调用==运算符。

它询问EqualityComparer<T>.Default实例是否相等。 反过来,它会在Equals实施中调用IEquatable,如果有,或Object.Equals方法作为最后的手段。

  

Equals的默认实现支持引用类型的引用相等,以及值类型的按位相等。

但是,如果您需要设置自己的相等规则,则可以自由覆盖该方法。

在您的示例中,parent如果是匿名类型。 C#编译器生成逐字段EqualsGetHashCode实现,因为显然您无法自己提供它们。

答案 3 :(得分:0)

每个.Net对象都实现IComparer接口,并拥有自己的CompareTo方法实现。 .Net只是使用这种方法来确定某些东西是否相等,在这种情况下.net只是检查两个对象的公共属性是否具有相同的值,因此它们是相等的。

编辑:抱歉,我把IComparer CompareTo与object.Equals混淆,每个对象实现了Equals方法,作为一个例子,String类重写了这个方法,只是检查两个字符串是否包含相同的值而不是引用相同的内存地址。

答案 4 :(得分:0)

要覆盖.Net的默认比较行为,您应该执行以下操作:

class A
{ 
    public long ParentId; 
    public string ParentName; 
    public string Name; 


    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != typeof(A))
            return false;
        var other=(A) obj;
        return Equals(other.ParentId, ParentId) && Equals(other.ParentName, ParentName);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (ParentName.GetHashCode() * 397) ^ ParentId.GetHashCode();
        }
    }
}