Eine tiefe Struktur artige Equals () für .NET-Klassen?
-
22-08-2019 - |
Frage
Da zwei einwenden, daß enthält keine Referenzschleife in sie, wissen Sie, eine Methode, die ihre Gleichheit in einer „allgemeinen“ Art und Weise testet (durch Reflexion)?
Ich mag im Grunde die gleiche Semantik wie struct Äquivalenz, nur auf Klassen.
Lösung
Ich denke, es gibt kein solches Verfahren im Rahmen vorhanden ist, aber es ist ziemlich leicht geschrieben. Vielleicht nicht die kürzeste Umsetzung aber scheint die Arbeit zu tun:
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;
}
Andere Tipps
Wenn Sie das tun möchten, ohne Reflexion bei jedem Aufruf zu tun, möchten Sie vielleicht den Aufbau einer DynamicMethod
auf den ersten Aufruf zu prüfen, und die Verwendung dieser statt. (Ich hatte einen Link zu dem Artikel, der dies tut, aber ich verlor es - leider -. Versuchen, wenn interessiert googeln)
BTW
Expression.Lambda<Func<T,T,bool>> Compile()
kann als dynamische Methode Builder verwendet werden.
noch Reflexion verwenden, während die Expresison Gebäude
Hier ist eine aktualisierte Version von Fredrik Mörk Antwort, die berücksichtigt Nullable
und rekursive Referenzen nimmt:
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;
}