Вопрос

Учитывая два объекта, которые не содержат внутри себя циклы ссылок, знаете ли вы метод, который проверяет их равенство «общим» способом (посредством отражения)?

По сути, мне нужна та же семантика, что и эквивалентность структур, только для классов.

Это было полезно?

Решение

Я думаю, что в рамках фреймворка такого метода нет, но его довольно легко написать.Возможно, это не самая короткая реализация, но, похоже, она справляется со своей задачей:

private bool AreEqual(object x, object y)
{
    // if both are null, they are equal
    if (x == null && y == null)
    {
        return true;
    }
    // if one of them are null, they are not equal
    else if (x == null || y == null)
    {
        return false;
    }

    // if they are of different types, they can't be compared
    if (x.GetType() != y.GetType())
    {
        throw new InvalidOperationException("x and y must be of the same type");
    }

    Type type = x.GetType();
    PropertyInfo[] properties = type.GetProperties();

    for (int i = 0; i < properties.Length; i++)
    {
        // compare only properties that requires no parameters
        if (properties[i].GetGetMethod().GetParameters().Length == 0)
        {
            object xValue = properties[i].GetValue(x, null);
            object yValue = properties[i].GetValue(y, null);

            if (properties[i].PropertyType.IsValueType && !xValue.Equals(yValue))
            {
                return false;
            }
            else if (!properties[i].PropertyType.IsValueType)
            {
                if (!AreEqual(xValue, yValue))
                {
                    return false;
                }
            } // if
        } // if
    } // for

    return true;

}

Другие советы

Если ты хочешь это сделать без размышляя над каждым вызовом, вы можете рассмотреть возможность создания DynamicMethod при первом вызове и вместо этого использовать его.(У меня была ссылка на статью, в которой это делается, но я ее потерял — извините — попробуйте погуглить, если интересно.)

КСТАТИ

 Expression.Lambda<Func<T,T,bool>> Compile()

может использоваться как построитель динамических методов.

все равно придется использовать отражение при построении выражения

Вот обновленная версия ответа Фредрика Мёрка, которая учитывает Nullable и рекурсивные ссылки:

public static bool AreEqual<T>(T x, T y) =>
    AreEqual(x, y, new HashSet<object>(new IdentityEqualityComparer<object>()));

private static bool AreEqual(object x, object y, ISet<object> visited)
{
    // if both are null, they are equal
    if (x == null && y == null) return true;

    // if one of them are null, they are not equal
    if (x == null || y == null) return false;

    // if they are of different types, they can't be compared
    if (x.GetType() != y.GetType())
    {
        throw new InvalidOperationException("x and y must be of the same type");
    }

    // check for recursive references
    if (visited.Contains(x)) return true;
    if (visited.Contains(y)) return true;
    visited.Add(x);
    visited.Add(y);

    var type = x.GetType();
    var properties = type.GetProperties();

    foreach (var property in properties)
    {
        // compare only properties that requires no parameters
        if (property.GetGetMethod().GetParameters().Length == 0)
        {
            object xValue = property.GetValue(x, null);
            object yValue = property.GetValue(y, null);

            if (property.PropertyType.IsValueType)
            {
                // check for Nullable
                if (xValue == null && yValue == null) continue;
                if (xValue == null || yValue == null) return false;
                if (!xValue.Equals(yValue)) return false;
            }

            if (!property.PropertyType.IsValueType)
            {
                if (!AreEqual(xValue, yValue, visited)) return false;
            }
        }
    }

    return true;
}

private class IdentityEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    public int GetHashCode(T value) => RuntimeHelpers.GetHashCode(value);
    public bool Equals(T left, T right) => left == right;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top