为POCO实现IEquatable [英] Implement IEquatable for POCO

查看:100
本文介绍了为POCO实现IEquatable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到EF的DbSet.Add()很慢。一个小小的谷歌搜索引起了一个答案,承诺高达180倍的性能提升:



https://stackoverflow.com/a/7052504/141172



但是,我不明白如何实现 IEquatable< ; T> 如答案中所建议的。



根据MSDN ,如果我实现 IEquatable< T> 我也应该覆盖 Equals() GetHashCode()



与许多POCO一样,我的对象是可变的。在提交到数据库之前( SaveChanges()),新对象的ID为0. 保存对象后,作为实现IEquatable,Equals()和GetHashCode()的理想基础。



在哈希码中包含任何mutable属性是不明智的,因为根据MSDN


如果两个对象比较相等,则每个
对象的GetHashCode方法必须返回相同的值


应该实现 IEquatable< T> 作为属性逐个比较(例如 this.FirstName == other.FirstName ),而不是覆盖Equals()和GetHashCode()?



鉴于我的POCO在EntityFramework上下文中使用,应该特别注意支付给Id字段?

解决方案

我遇到了你的问题,寻找同样的问题的解决方案。这是一个我正在尝试的解决方案,看看它是否满足您的需求:



首先,我所有的POCO都源于这个抽象类:

  public abstract class BasePOCO< T> :IEquatable< T>其中T:class 
{
private readonly Guid _guid = Guid.NewGuid();

#region IEquatable< T>成员

public abstract bool等于(T other);

#endregion

public override bool Equals(object obj)
{
if(ReferenceEquals(null,obj))
{
返回false;
}
if(ReferenceEquals(this,obj))
{
return true;
}
if(obj.GetType()!= typeof(T))
{
return false;
}
return Equals((T)obj);
}

public override int GetHashCode()
{
return _guid.GetHashCode();
}
}

我创建了一个只读Guid字段, GetHashCode()覆盖。这将确保我将派生的POCO放入字典或使用哈希的其他东西,如果我在临时调用了.SaveChanges()并且ID字段被基类更新,那么我不会孤立>这是我不确定是完全正确的一部分,还是比Base.GetHashCode()?更好。我抽象了Equals(T other)方法,以确保实现类必须以有意义的方式实现它,最有可能是ID字段。我把Equals(object obj)重写在这个基类中,因为它对所有的派生类也可能是一样的。



这将是抽象的一个实现类:

  public class种类:BasePOCO< Species> 
{
public int ID {get;组; }
public string LegacyCode {get;组; }
public string Name {get;组;

public override bool Equals(Species other)
{
if(ReferenceEquals(null,other))
{
return false;
}
if(ReferenceEquals(this,other))
{
return true;
}
返回ID!= 0&&&
ID == other.ID&&&
LegacyCode == other.LegacyCode&&&
名称== other.Name;
}
}

ID属性设置为数据库和EF知道。对于新创建的对象,ID为0,然后在.SaveChanges()上设置为唯一的正整数。所以在重写的Equals(Species other)方法中,空对象显然不相等,相同的引用显然是,那么我们只需要检查ID == 0。如果是的话,我们会说两个相同类型的对象这两个ID都为0不相等。否则,我们会说他们的财产是一样的。



我认为这涵盖了所有相关情况,但是如果我不正确,请进来。希望这有帮助。



===编辑1



我在想我的GetHashCode()不正确我查看了关于该主题的 https://stackoverflow.com/a/371348/213169 。上面的实现将违反返回Equals()== true的对象的约束必须具有相同的哈希码。



这是我的第二个刺:

  public abstract class BasePOCO< T> :IEquatable< T>其中T:class 
{
#region IEquatable< T>成员

public abstract bool等于(T other);

#endregion

public abstract override bool Equals(object obj);
public abstract override int GetHashCode();
}

执行:



public class种类:BasePOCO< Species>
{
public int ID {get;组; }
public string LegacyCode {get;组; }
public string Name {get;组;

public override bool Equals(Species other)
{
if(ReferenceEquals(null,other))
{
return false;
}
if(ReferenceEquals(this,other))
{
return true;
}
返回ID!= 0&&&
ID == other.ID&&&
LegacyCode == other.LegacyCode&&&
名称== other.Name;


public override bool Equals(object obj)
{
if(ReferenceEquals(null,obj))
{
return false ;
}
if(ReferenceEquals(this,obj))
{
return true;
}
return Equals(obj as Species);
}

public override int GetHashCode()
{
unchecked
{
return((LegacyCode!= null?LegacyCode.GetHashCode ):0)* 397)^
(Name!= null?Name.GetHashCode():0);
}
}

public static bool operator ==(Species left,Species right)
{
return Equals(left,right);
}

public static bool operator!=(物种离开,物种正确)
{
return!等于(左,右);
}
}

所以我在基类中摆脱了Guid并将GetHashCode移动到实现中。我使用Resharper的GetHashCode实现除了ID之外的所有属性,因为ID可能会改变(不要孤儿)。这将满足上述链接答案中对平等的约束。


I noticed that EF's DbSet.Add() is quite slow. A little googling turned up a SO answer that promises up to 180x performance gains:

https://stackoverflow.com/a/7052504/141172

However, I do not understand exactly how to implement IEquatable<T> as suggested in the answer.

According to MSDN, if I implement IEquatable<T>, I should also override Equals() and GetHashCode().

As with many POCO's, my objects are mutable. Before being committed to the database (SaveChanges()), new objects have an Id of 0. After the objects have been saved, the Id serves as an ideal basis for implementing IEquatable, Equals() and GetHashCode().

It is unwise to include any mutable property in a hash code, and since according to MSDN

If two objects compare as equal, the GetHashCode method for each object must return the same value

Should I implement IEquatable<T> as a property-by-property comparison (e.g. this.FirstName == other.FirstName) and not override Equals() and GetHashCode()?

Given that my POCO's are used in an EntityFramework context, should any special attention be paid to the Id field?

解决方案

I came across your question in search for a solution to the same question. Here is a solution that I am trying out, see if it meets your needs:

First, all my POCOs derive from this abstract class:

public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
    private readonly Guid _guid = Guid.NewGuid();

    #region IEquatable<T> Members

    public abstract bool Equals(T other);

    #endregion

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        if (obj.GetType() != typeof (T))
        {
            return false;
        }
        return Equals((T)obj);
    }

    public override int GetHashCode()
    {
        return _guid.GetHashCode();
    }
}

I created a readonly Guid field that I am using in the GetHashCode() override. This will ensure that were I to put the derived POCO into a Dictionary or something else that uses the hash, I would not orphan it if I called a .SaveChanges() in the interim and the ID field was updated by the base class This is the one part I'm not sure is completely correct, or if it is any better than just Base.GetHashCode()?. I abstracted the Equals(T other) method to ensure the implementing classes had to implement it in some meaningful way, most likely with the ID field. I put the Equals(object obj) override in this base class because it would probably be the same for all the derived classes too.

This would be an implementation of the abstract class:

public class Species : BasePOCO<Species>
{
    public int ID { get; set; }
    public string LegacyCode { get; set; }
    public string Name { get; set; }

    public override bool Equals(Species other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }
        if (ReferenceEquals(this, other))
        {
            return true;
        }
        return ID != 0 && 
               ID == other.ID && 
               LegacyCode == other.LegacyCode &&
               Name == other.Name;
    }
}

The ID property is set as the primary key in the Database and EF knows that. ID is 0 on a newly created objects, then gets set to a unique positive integer on .SaveChanges(). So in the overridden Equals(Species other) method, null objects are obviously not equal, same references obviously are, then we only need to check if the ID == 0. If it is, we will say that two objects of the same type that both have IDs of 0 are not equal. Otherwise, we will say they are equal if their properties are all the same.

I think this covers all the relevant situations, but please chime in if I am incorrect. Hope this helps.

=== Edit 1

I was thinking my GetHashCode() wasn't right, and I looked at this https://stackoverflow.com/a/371348/213169 answer regarding the subject. The implementation above would violate the constraint that objects returning Equals() == true must have the same hashcode.

Here is my second stab at it:

public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
    #region IEquatable<T> Members

    public abstract bool Equals(T other);

    #endregion

    public abstract override bool Equals(object obj);
    public abstract override int GetHashCode();
}

And the implementation:

public class Species : BasePOCO<Species>
{
    public int ID { get; set; }
    public string LegacyCode { get; set; }
    public string Name { get; set; }

    public override bool Equals(Species other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }
        if (ReferenceEquals(this, other))
        {
            return true;
        }
        return ID != 0 && 
        ID == other.ID && 
        LegacyCode == other.LegacyCode && 
        Name == other.Name;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        return Equals(obj as Species);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((LegacyCode != null ? LegacyCode.GetHashCode() : 0) * 397) ^ 
                   (Name != null ? Name.GetHashCode() : 0);
        }
    }

    public static bool operator ==(Species left, Species right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(Species left, Species right)
    {
        return !Equals(left, right);
    }
}

So I got rid of the Guid in the base class and moved GetHashCode to the implementation. I used Resharper's implementation of GetHashCode with all the properties except ID, since ID could change (don't want orphans). This will meet the constraint on equality in the linked answer above.

这篇关于为POCO实现IEquatable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆