通用平等检查器
General Purpose Equality Checker
我正在维护一个代码库,我发现了两个扩展方法,处理检查对象的结构相等性,我根本不喜欢这种方法(一个调用另一个):
public static bool IsObjectEqual<T>(this T obj, T obj2)
{
foreach (var pi in obj.GetType().GetProperties())
{
var enumerable1 = pi.GetValue(obj, null) as IEnumerable;
var enumerable2 = pi.GetValue(obj2, null) as IEnumerable;
if (enumerable1 != null)
{
foreach (var item in enumerable1)
{
if (enumerable2 == null || !((object[])enumerable2).Contains(item))
{
return false;
}
}
}
else if (!IsPropertyInfoValueEqual(pi, obj, obj2))
{
return false;
}
}
return true;
}
private static bool IsPropertyInfoValueEqual<T>(PropertyInfo pi, T obj, T obj2)
{
var val = pi.GetValue(obj, null);
var val2 = pi.GetValue(obj2, null);
if (val != null && val2 != null)
{
return val.Equals(val2);
}
return (val == null && val2 == null);
}
问题是,我无法想出我认为它们会失败或导致问题的场景。我知道在我的水域中他们是错误的,我只是不能指手画脚。
我在实施相等性检查时一直使用 IEquateable<T>
,因此缺少任何处理相等性的框架 classes/Interfaces 是一件事唤起我的狡猾感觉。我意识到这种方法试图成为一种通用的、基于反射的方法,但它让我感到紧张(如前所述)。
任何人都可以看出这些方法的合理问题吗?
编辑
这些方法存在很大的合法问题。转换为 object[] 会导致 InvalidCastException。
可能还有其他问题,但我发现此代码有两个主要问题:
反射是昂贵的。真的很贵。将它大规模地用于像相等性检查这样简单的事情只是一个坏主意。该代码甚至不缓存对象图,因此它必须每次 进行完全反射。在相当常见的情况下,我可以看到这种方法是性能瓶颈。
如果成员是 IEnumerable
,代码将无法按预期工作。其他一切都与反射进行比较,但集合使用 Contains
进行比较。这将执行简单的引用相等(至少对于引用类型)并且不重用反射方法。对于使用此方法的用户来说,这可能会导致不良结果。
实施 IEquatable
and friends 是一种更快、更安全的方法。实施者可以明确决定比较的工作方式,您不需要任何反思。
我正在维护一个代码库,我发现了两个扩展方法,处理检查对象的结构相等性,我根本不喜欢这种方法(一个调用另一个):
public static bool IsObjectEqual<T>(this T obj, T obj2)
{
foreach (var pi in obj.GetType().GetProperties())
{
var enumerable1 = pi.GetValue(obj, null) as IEnumerable;
var enumerable2 = pi.GetValue(obj2, null) as IEnumerable;
if (enumerable1 != null)
{
foreach (var item in enumerable1)
{
if (enumerable2 == null || !((object[])enumerable2).Contains(item))
{
return false;
}
}
}
else if (!IsPropertyInfoValueEqual(pi, obj, obj2))
{
return false;
}
}
return true;
}
private static bool IsPropertyInfoValueEqual<T>(PropertyInfo pi, T obj, T obj2)
{
var val = pi.GetValue(obj, null);
var val2 = pi.GetValue(obj2, null);
if (val != null && val2 != null)
{
return val.Equals(val2);
}
return (val == null && val2 == null);
}
问题是,我无法想出我认为它们会失败或导致问题的场景。我知道在我的水域中他们是错误的,我只是不能指手画脚。
我在实施相等性检查时一直使用 IEquateable<T>
,因此缺少任何处理相等性的框架 classes/Interfaces 是一件事唤起我的狡猾感觉。我意识到这种方法试图成为一种通用的、基于反射的方法,但它让我感到紧张(如前所述)。
任何人都可以看出这些方法的合理问题吗?
编辑
这些方法存在很大的合法问题。转换为 object[] 会导致 InvalidCastException。
可能还有其他问题,但我发现此代码有两个主要问题:
反射是昂贵的。真的很贵。将它大规模地用于像相等性检查这样简单的事情只是一个坏主意。该代码甚至不缓存对象图,因此它必须每次 进行完全反射。在相当常见的情况下,我可以看到这种方法是性能瓶颈。
如果成员是
IEnumerable
,代码将无法按预期工作。其他一切都与反射进行比较,但集合使用Contains
进行比较。这将执行简单的引用相等(至少对于引用类型)并且不重用反射方法。对于使用此方法的用户来说,这可能会导致不良结果。
实施 IEquatable
and friends 是一种更快、更安全的方法。实施者可以明确决定比较的工作方式,您不需要任何反思。