هل يشبه البنية العميقة Equals() لفئات .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
عند الاستدعاء الأول واستخدام ذلك بدلاً من ذلك.(كان لدي رابط للمقال الذي يفعل ذلك، لكنني فقدته - آسف - حاول البحث في Google إذا كنت مهتمًا.)
وراجع للشغل
Expression.Lambda<Func<T,T,bool>> Compile()
ويمكن استخدامها كوسيلة من وسائل البناء الحيوية.
لا يزال لديك لاستخدام انعكاس حين بناء Expresison
وهنا نسخة محدثة من الإجابة فريدريك مورك أن يأخذ بعين الاعتبار 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;
}