【问题标题】:How .NET determines equal objects in LINQ "select new"?.NET 如何确定 LINQ“选择新”中的相等对象?
【发布时间】:2011-07-31 09:23:42
【问题描述】:

示例如下:

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

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

【问题讨论】:

    标签: .net linq equals equality


    【解决方案1】:

    这是如何实现 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
    

    【讨论】:

      【解决方案2】:

      GroupBy使用标准的hash+equals方法(常见于HashtableDictionary&lt;,&gt;等),也就是说:

      • GetHashCode 定义了明确的不相等(不同时)和可能相等(相同时)
      • 等于(IEquatable&lt;T&gt;.Equalsobject.Equals)定义相等

      您的投影分组比较ParentName,因此在比较项目时使用。由于string 已明确定义GetHashCode/Equals,因此可以正常工作。

      关于你的最后一个问题:

      我想,new 在源集合的每次迭代中都会创建新对象。

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 这是错误的。 System.Object doesn't 实现 IComparable (这可能是你的意思)。
        • A 的两个不同实例如果它们的字段相等,则它们将不相等。
        【解决方案4】:

        要覆盖 .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();
                }
            }
        }
        

        【讨论】:

        • 这不是最好的方法。相反,您应该实现IEquatable&lt;A&gt; 并从“普通对象”Equals 调用它的Equals
        【解决方案5】:

        它不调用== 运算符。

        它询问EqualityComparer&lt;T&gt;.Default 实例是否相等。 反过来,它会从IEquatable 实现中调用Equals(如果有),或者作为最后的手段调用Object.Equals 方法。

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

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

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

        【讨论】:

        • 你的意思是,在这种情况下parent 是值类型?
        猜你喜欢
        • 1970-01-01
        • 2011-01-07
        • 1970-01-01
        • 1970-01-01
        • 2011-06-29
        • 2010-09-17
        相关资源
        最近更新 更多