I've an object that inherites from a base class that inherits from IEquatable<>. So far so good and it works for other objects that inherit the same base class. But I have on class "RoomType" where there seems to be a problem when I use the "Attrbiutes" property. Below you see the classes and a test where I expect other output that given.
I narrowed the problem down to something with the RoomType.GetHashCode(), when I comment "SafeHashCode(Attributes)" the expected result is returned.
Test:
private static void QuickTest()
{
RoomType[] rooms = new RoomType[] {
new RoomType {
Attributes = new [] { "a", "b,"c"},
},
new RoomType
{
Attributes = new [] { "a", "b","c"},
}
};
List<RoomType> result = rooms.Distinct().ToList();
//result contains 2 items, I was expecting 1
}
RoomType:
public class RoomType : EntityBase
{
public string OriginalRoomCode { get; set; }
public Enum.RoomType RoomCode { get; set; }
public IEnumerable<string> Attributes { get; set; }
public override bool Equals(object obj)
{
RoomType other = obj as RoomType;
if (other != null)
return Equals(other);
return false;
}
public override bool Equals(EntityBase obj)
{
RoomType y = (RoomType)obj;
return SafeEqual(OriginalRoomCode, y.OriginalRoomCode) &&
SafeEqual(RoomCode, y.RoomCode) &&
SafeEqual(Attributes,y.Attributes);
}
public override int GetHashCode()
{
unchecked
{
return SafeHashCode(OriginalRoomCode) ^
SafeHashCode(RoomCode) ^
SafeHashCode(Attributes);
}
}
public override object Clone()
{
return new RoomType
{
RoomCode = (Enum.RoomType)SafeClone(RoomCode),
OriginalRoomCode = (string)SafeClone(OriginalRoomCode),
Attributes = (IEnumerable<string>)SafeClone(Attributes)
};
}
}
EntityBase:
public abstract class EntityBase : IEquatable<EntityBase>, ICloneable
{
public bool SafeEqual<T>(T x, T y)
{
bool isXDefault = EqualityComparer<T>.Default.Equals(x, default(T));
bool isYDefault = EqualityComparer<T>.Default.Equals(y, default(T));
if (isXDefault && isYDefault)
return true;
if (isXDefault != isYDefault)
return false;
if (x is EntityBase)
return x.Equals(y);
IEnumerable<object> xEnumerable = x as IEnumerable<object>;
IEnumerable<object> yEnumerable = y as IEnumerable<object>;
if (xEnumerable != null && yEnumerable != null)
{
foreach (var yItem in yEnumerable)
{
bool match = false;
foreach (var xItem in xEnumerable)
{
if(SafeEqual(xItem, yItem))
{
match = true;
break;
}
}
if (!match)
return false;
}
return true;
}
return x.Equals(y);
}
public int SafeHashCode<T>(T x)
{
if (EqualityComparer<T>.Default.Equals(x, default(T)))
return 0;
return x.GetHashCode();
}
public object SafeClone<T>(T x)
{
//if x is null or default value
if (EqualityComparer<T>.Default.Equals(x, default(T)))
return default(T);
//if x is of type EntityBase call clone()
if (x is EntityBase)
return (x as EntityBase).Clone();
//else the type is a default type return the value
return x;
}
public abstract bool Equals(EntityBase other);
public override abstract int GetHashCode();
public abstract override bool Equals(object obj);
public abstract object Clone();
}
Update I was able to fix it by adding the following code inside SafeHashCode(T x)
IEnumerable<object> xEnumerable = x as IEnumerable<object>;
if (xEnumerable != null)
return xEnumerable.Aggregate(17, (acc, item) => acc * 19 + SafeHashCode(item));