Reputation: 888
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
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
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