vantian
vantian

Reputation: 888

Dictionary compare between two objects

Assuming I have these objects

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

I want to check the equality of those object by using dictionary, so it's smiliar like this

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

However, it's always return as False. what I'm missing here?

EDIT

Add custom dictionary

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

Add Classes

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

Upvotes: 0

Views: 118

Answers (2)

ashin
ashin

Reputation: 2603

Because you create new references of Address and Person, it wouldn't match the key, The following Assert statements would work:

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

UPDATE

If new references need to be created, make use of EqualityComparer argument for the dictionary:

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

And then in your test:

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

It is recommended to use the generic type instead of using the object in dictionary; Consider changing your key from object to a type.

Upvotes: 1

bashrc
bashrc

Reputation: 4835

You need to pass a instance of an appropriate IEqualityComparer. If you do not pass any comparer dictionary will use the default comparer. (EqualityComparer.Default). This will only return true if the parameter passed are same instance of the object. You will have to create your own comparer

Or you can override GetHashCode and Equals in all classes that you want to store in the dictionary. Then EqualityComparer.Default calls the appropriate overrides for GetHashCode and Equals and you are set. If you are overriding these two functions you don't need to create or pass a comparer to the dictionary.

Edit based on OP's edit:

Note that your implementation of GetHashCode should be consistent with implementation of Equals:

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

In your case since you are still using the objects default GetHashCode the above condition doesn't holds.

The GetHashCode should be implemented like:

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

Upvotes: 2

Related Questions