通用平等检查器

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。

可能还有其他问题,但我发现此代码有两个主要问题:

  1. 反射是昂贵的。真的很贵。将它大规模地用于像相等性检查这样简单的事情只是一个坏主意。该代码甚至不缓存对象图,因此它必须每次 进行完全反射。在相当常见的情况下,我可以看到这种方法是性能瓶颈。

  2. 如果成员是 IEnumerable,代码将无法按预期工作。其他一切都与反射进行比较,但集合使用 Contains 进行比较。这将执行简单的引用相等(至少对于引用类型)并且不重用反射方法。对于使用此方法的用户来说,这可能会导致不良结果。

实施 IEquatable and friends 是一种更快、更安全的方法。实施者可以明确决定比较的工作方式,您不需要任何反思。