.NET 클래스에 대한 깊은 구조와 같은 동등?
-
22-08-2019 - |
문제
그 안에는 참조 루프가 포함되어 있지 않은 두 가지 대상을 감안할 때 (반사를 통해) "일반적인"방식으로 평등을 테스트하는 방법을 알고 있습니까?
나는 기본적으로 수업에서만 구조물 동등성과 동일한 의미를 원합니다.
해결책
프레임 워크에는 그러한 방법이 없다고 생각하지만 상당히 쉽게 작성됩니다. 아마도 가장 짧은 구현은 아니지만 일을하는 것 같습니다.
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
첫 번째 호출 및 대신 사용합니다. (나는 이것을하는 기사에 대한 링크가 있었지만 그것을 잃어 버렸습니다 - 죄송합니다 - 관심이 있으면 인터넷 검색을 시도하십시오.)
BTW
Expression.Lambda<Func<T,T,bool>> Compile()
동적 메소드 빌더로 사용할 수 있습니다.
Expreresison을 구축하는 동안 여전히 반사를 사용해야합니다
다음은 Fredrik Mörk의 답변에서 업데이트 된 버전입니다. 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;
}
제휴하지 않습니다 StackOverflow