我从一个继承自IEquatable<>的基类继承的对象。到目前为止这么好,它适用于继承相同基类的其他对象。但是我上课了#34; RoomType"当我使用" Attrbiutes"时似乎有问题。属性。下面你会看到我希望得到其他输出的类和测试。
当我评论" SafeHashCode(Attributes)"时,我将问题缩小到使用RoomType.GetHashCode()。返回预期的结果。
测试:
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();
}
更新 我能够通过在SafeHashCode(T x)
中添加以下代码来解决它 IEnumerable<object> xEnumerable = x as IEnumerable<object>;
if (xEnumerable != null)
return xEnumerable.Aggregate(17, (acc, item) => acc * 19 + SafeHashCode(item));
答案 0 :(得分:2)
问题是2个数组的哈希码将不相同,即使内容可以被认为是相等的。
而不是将数组传递给SafeHashCode
获取数组中每个成员的哈希码。
public override int GetHashCode()
{
unchecked
{
return SafeHashCode(OriginalRoomCode) ^
SafeHashCode(RoomCode) ^
Attributes.Select(x => SafeGetHashCode(x)).Aggregate((seed, current) => seed ^ current);
}
}
答案 1 :(得分:1)
对于SaveEqual
,您可以自定检查IEnumerable
类型比较 - 您可以检查xEnumerable
中的每个项目是否包含在yEnumerable
中。 注1 :此处也有错误 - yEnumerable
可以包含其他项目,也可能包含重复项目。
但对于SaveHashCode
,您没有IEnumerable
类型的自定义处理。您只需返回参数的哈希码。即使数组包含相同的值,这也会为不同的数组实例提供不同的结果。要解决此问题,您应该根据集合项计算哈希码:
public int SaveHashCode<T>(T x)
{
if (EqualityComparer<T>.Default.Equals(x, default(T)))
return 0;
IEnumerable<object> xEnumerable = x as IEnumerable<object>;
if (xEnumerable != null)
return xEnumerable.Aggregate(17, (acc, item) => acc * 19 + SaveHashCode(item));
return x.GetHashCode();
}
注意2 使用XOR进行hashCode计算会给您带来另一个问题 - XORing为零(您使用的是默认值,或者它可以是整数0
的哈希码,或者是布尔值false
)不会修改结果。结果也不取决于订单。即如果您有两个数组:[0, 1, 2]
和[2,0,1,0]
。在@christophano建议的XORing会给你...... 3
和3
。但那些阵列完全不同。所以我建议你使用hashCode计算based on prime numbers。