【问题标题】:Dictionary compare between two objects两个对象之间的字典比较
【发布时间】:2017-07-07 04:37:02
【问题描述】:

假设我有这些对象

    var address = new Address("5455 Apache Trail", "Queen Creek", "AZ", "85243");
    var person = new Person("Jane", "Smith", address);

我想通过字典检查那些对象的相等性,所以它就像这样

    var dictionary = new Dictionary<object>{ [address] = address, [person] = person};
    Assert.IsTrue(dictionary.ContainsKey(new Address("5455 Apache Trail", "Queen Creek", "AZ", "85243")));
    Assert.IsTrue(dictionary.ContainsKey(new Person("Jane", "Smith", address)));

但是,它总是返回 False。我在这里缺少什么?

编辑

添加自定义词典

public class Dictionary<T> : Dictionary<T, T> where T : class, new()
{
}

添加类

public abstract class BaseModel
{
    public string Id { get; set; }

    public BaseModel()
    {
    }
}

public class Address : BaseModel
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }

    public Address() { }

    public override bool Equals(object value)
    {
        if (value == null)
            return false;

        Address mod = value as Address;

        return (mod != null)
            && (Street == mod.Street)
            && (City == mod.City)
            && (PostalCode == mod.PostalCode)
            && (State == mod.State);
    }

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

    public Address(string street, string city, string state, string postalCode) {
        this.Street = street;
        this.City = city;
        this.State = state;
        this.PostalCode = postalCode;
    }
}

public class Person : BaseModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }

    public Person() { }

    public Person(string firstName, string lastName, Address address)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
        this.Address = address;
    }

    public override bool Equals(object value)
    {
        if (value == null)
            return false;

        Person mod = value as Person;

        return (mod != null)
            && (FirstName == mod.FirstName)
            && (LastName == mod.LastName)
            && (Address.Equals(mod.Address));
    }


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

【问题讨论】:

  • @DavidL 这在 C# 6 中运行良好,我也更新了我的问题以拥有一个自定义字典

标签: c# asp.net dictionary


【解决方案1】:

您需要传递一个适当的IEqualityComparer 的实例。 如果您不传递任何比较器字典将使用默认比较器。 (EqualityComparer.Default)。如果传递的参数是对象的相同实例,这只会返回 true。您必须创建自己的比较器

或者您可以在要存储在字典中的所有类中覆盖 GetHashCode 和 Equals。然后 EqualityComparer.Default 为 GetHashCodeEquals 调用适当的覆盖,然后您就设置好了。如果您要覆盖这两个函数,则不需要创建比较器或将比较器传递给字典。

根据 OP 的编辑进行编辑:

请注意,GetHashCode 的实现应该与 Equals 的实现一致:

a.Equals(b) => b.Equals(a) => a.GetHashCode() == b.GetHashCode()

在您的情况下,由于您仍在使用对象默认 GetHashCode,因此上述条件不成立。

GetHashCode 应该这样实现:

  public override int GetHashCode(){
        return this.FirstName.GetHashCode() ^ 
               this.LastName.GetHashCode() ^ 
               this.Address.GetHashCode(); 
    }

【讨论】:

  • 我在我的课程上都覆盖了这两个。你可以在这里查看dotnetfiddle.net/v5wEji
  • 返回 base.GetHashCode();这是错误的。这仍然会获得对象的通用哈希码,它将返回对象的不同实例。您需要更改它以使用您的人员和地址对象字段中的哈希码。
  • 太棒了,它的作品。你有一个链接来了解更多吗?我对 EqualityComparer 以及如何正确使用 Equals()GetHashCode() 有点迷茫。
【解决方案2】:

因为您创建了 Address 和 Person 的新引用,它不会匹配键,以下 Assert 语句将起作用:

 var dictionary = new Dictionary<object, object> { [address] = address, [person] = person };
 Assert.IsTrue(dictionary.ContainsKey(address));
 Assert.IsTrue(dictionary.ContainsKey(person));

更新

如果需要创建新的引用,请使用字典的 EqualityComparer 参数:

public class CustomEqualityComparer : IEqualityComparer<object>
{
    public new bool Equals(object x, object y)
    {
        if (x is Address && y is Address)
        {
            var xAddress = x as Address;
            var yAddress = y as Address;
            return xAddress.Line1 == yAddress.Line1 &&
               xAddress.Line2 == yAddress.Line2 &&
               xAddress.Line3 == yAddress.Line3 &&
               xAddress.Line4 == yAddress.Line4;
        }

        if (x is Person && y is Person)
        {
            var xPerson = x as Person;
            var yPerson = y as Person;
            return xPerson.FirstName == yPerson.FirstName &&
               xPerson.LastName == yPerson.LastName;
        }

        return false;
    }

    public int GetHashCode(object obj)
    {
        if (obj is Address)
        {
            var address = obj as Address;
            return  address.Line1.GetHashCode() ^
                    address.Line2.GetHashCode() ^
                    address.Line3.GetHashCode() ^
                    address.Line4.GetHashCode();
        }

        if (obj is Person)
        {
            var person = obj as Person;
            return person.FirstName.GetHashCode() ^
                   person.LastName.GetHashCode();
        }

        return obj.GetHashCode();
    }
}

然后在你的测试中:

var dictionary = new Dictionary<object, object>(new CustomEqualityComparer())
        { [address] = address, [person] = person };
        Assert.IsTrue(dictionary.ContainsKey(new Address("5455 Apache Trail", "Queen Creek", "AZ", "85243")));
        Assert.IsTrue(dictionary.ContainsKey(person));

建议使用泛型类型,而不是使用字典中的对象;考虑将键从对象更改为类型。

【讨论】:

  • 我必须添加一个创建新引用。所以我可以在两个不同的对象之间进行检查
  • 请找到更新的答案,您基本上必须让字典知道如何比较对象
  • @ashin 虽然这与当前问题无关,但您答案中的 GetHashCode 可能会导致某些类型的数据出现性能问题。 (例如 {"bashrc", "real"} 将给出与 {"bashrcreal", ""} 相同的哈希码。)相同的哈希码进入同一个桶并进行线性搜索。
  • 你是对的..感谢您指出!我已经更新了答案。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-03
  • 2016-09-07
  • 1970-01-01
  • 1970-01-01
  • 2015-07-26
相关资源
最近更新 更多