Implementing correct GetHashCode
I have the following class
public class ResourceInfo
{
public string Id { get; set; }
public string Url { get; set; }
}
which contains information about some resource. Now I need the possibility to check if two such resources are equal by the following scenario (I`ve implemented IEquatable interface)
public class ResourceInfo : IEquatable<ResourceInfo>
{
public string Id { get; set; }
public string Url { get; set; }
public bool Equals(ResourceInfo other)
{
if (other == null)
return false;
// Try to match by Id
if (!string.IsNullOrEmpty(Id) && !string.IsNullOrEmpty(other.Id))
{
return string.Equals(Id, other.Id, StringComparison.InvariantCultureIgnoreCase);
}
// Match by Url if can`t match by Id
return string.Equals(Url, other.Url, StringComparison.InvariantCultureIgnoreCase);
}
}
Usage: oneResource.Equals(otherResource)
. And everything is just fine. But some time have passed and now I need to use such eqaulity comparing in some linq query.
As a result I need to implement separate Equality comparer which looks like this:
class ResourceInfoEqualityComparer : IEqualityComparer<ResourceInfo>
{
public bool Equals(ResourceInfo x, ResourceInfo y)
{
if (x == null || y == null)
return object.Equals(x, y);
return x.Equals(y);
}
public int GetHashCode(ResourceInfo obj)
{
if (obj == null)
return 0;
return obj.GetHashCode();
}
}
Seems to be ok: it makes some validation logic and uses the native equality comparing logic. But then I need to implement GetHashCode method in the ResourceInfo
class and that is the place where I have some problem.
I don`t know how to do this correctly without changing the class itself.
At first glance, the following example can work
public override int GetHashCode()
{
// Try to get hashcode from Id
if(!string.IsNullOrEmpty(Id))
return Id.GetHashCode();
// Try to get hashcode from url
if(!string.IsNullOrEmpty(Url))
return Url.GetHashCode();
// Return zero
return 0;
}
But this implementation is not very good.
GetHashCode should match the Equals method : if two objects are equal, then they should have the same hashcode, right? But my Equals method uses two objects to compare them. Here is the usecase, where you can see the problem itself:
var resInfo1 = new ResourceInfo()
{
Id = null,
Url = "http://res.com/id1"
};
var resInfo2 = new ResourceInfo()
{
Id = "id1",
Url = "http://res.com/id1"
};
So, what will happen, when we invoke Equals method: obviously they will be equal, because Equals method will try to match them by Id and fail, then it tries matching by Url and here we have the same values. As intended.
resInfo1.Equals(resInfo1 ) -> true
But then, if they are equal, they should have the same hash codes:
var hash1 = resInfo.GetHashCode(); // -263327347
var hash2 = resInfo.GetHashCode(); // 1511443452
hash1.GetHashCode() == hash2.GetHashCode() -> false
Shortly speaking, the problem is that Equals
method decides which field to use for equality comparing by looking at two different objects, while GetHashCode
method have access only to one object.
Is there a way to implement it correctly or I just have to change my class to avoid such situations?
Many thanks.
Your approach to equality fundamentally breaks the specifications in Object.Equals
.
In particular, consider:
var x = new ResourceInfo { Id = null, Uri = "a" };
var y = new ResourceInfo { Id = "yz", Uri = "a" };
var z = new ResourceInfo { Id = "yz", Uri = "b" };
Here, x.Equals(y)
would be true, and y.Equals(z)
would be true - but x.Equals(z)
would be false. That is specifically prohibited in the documentation:
(x.Equals(y) && y.Equals(z))
returns true, then x.Equals(z)
returns true. You'll need to redesign, basically.
链接地址: http://www.djcxy.com/p/39742.html上一篇: 有没有办法从IComparer派生IEqualityComparer?
下一篇: 实现正确的GetHashCode