كيف يمكنني التحقق من وجود القيم الخالية في التحميل الزائد لعامل التشغيل '==' دون تكرار لا نهائي؟
-
09-06-2019 - |
سؤال
سيؤدي ما يلي إلى التكرار اللانهائي على طريقة التحميل الزائد للمشغل ==
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (foo1 == null) return foo2 == null;
return foo1.Equals(foo2);
}
كيف يمكنني التحقق من وجود القيم الخالية؟
المحلول
يستخدم ReferenceEquals
:
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (object.ReferenceEquals(null, foo1))
return object.ReferenceEquals(null, foo2);
return foo1.Equals(foo2);
}
نصائح أخرى
الإرسال إلى الكائن بطريقة التحميل الزائد:
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
يستخدم مرجع يساوي
.من منتديات MSDN:
public static bool operator ==(Foo foo1, Foo foo2) {
if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
if (ReferenceEquals(foo2, null)) return false;
return foo1.field1 == foo2.field2;
}
يحاول Object.ReferenceEquals(foo1, null)
على أية حال، لا أنصح بالإفراط في التحميل ==
المشغل أو العامل؛يجب استخدامه لمقارنة المراجع واستخدامها Equals
للمقارنات "الدلالية".
إذا لقد تجاوزت bool Equals(object obj)
وأنا أريد المشغل ==
و Foo.Equals(object obj)
لإرجاع نفس الإجابة، عادةً ما أقوم بتنفيذ الأمر !=
عامل مثل هذا:
public static bool operator ==(Foo foo1, Foo foo2) {
return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
return !object.Equals(foo1, foo2);
}
المشغل ==
بعد ذلك، بعد إجراء جميع عمليات التحقق الفارغة بالنسبة لي، سينتهي بي الأمر بالاتصال foo1.Equals(foo2)
لقد تجاوزت إجراء الفحص الفعلي إذا كان الاثنان متساويين.
إذا كنت تستخدم C# 7 أو إصدارًا أحدث، فيمكنك استخدام مطابقة النمط الثابت الفارغ:
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
يمنحك هذا رمزًا أكثر إتقانًا قليلاً من الكائن المتصل.ReferenceEquals(foo1, null)
نهجي هو القيام به
(object)item == null
الذي أعتمد عليه object
عامل المساواة الخاص الذي لا يمكن أن يخطئ.أو طريقة تمديد مخصصة (والتحميل الزائد):
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null;
}
public static bool IsNull<T>(this T? obj) where T : struct
{
return !obj.HasValue;
}
أو للتعامل مع المزيد من الحالات، قد يكون:
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
القيد يمنع IsNull
على أنواع القيمة.الآن أصبح الأمر رائعًا مثل الاتصال
object obj = new object();
Guid? guid = null;
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error
مما يعني أن لدي أسلوبًا ثابتًا/غير معرض للخطأ للتحقق من القيم الخالية طوال الوقت.لقد وجدت أيضا (object)item == null
أسرع قليلاً جداً جداً من Object.ReferenceEquals(item, null)
, ، ولكن فقط إذا كان الأمر مهمًا (أعمل حاليًا على شيء حيث يتعين علي تحسين كل شيء بشكل دقيق!).
للاطلاع على دليل كامل حول تنفيذ ضوابط المساواة، راجع ما هي "أفضل الممارسات" لمقارنة مثيلين من نوع مرجعي؟
الساكنة Equals(Object, Object)
طريقة يشير إلى ما إذا كان هناك كائنين، objA
و objB
, ، متساوون.كما أنه يمكّنك من اختبار الكائنات التي تكون قيمتها null
من أجل المساواة.يقارن objA
و objB
للمساواة على النحو التالي:
- فهو يحدد ما إذا كان الكائنان يمثلان نفس مرجع الكائن.إذا فعلوا ذلك، يعود الأسلوب
true
.هذا الاختبار يعادل استدعاءReferenceEquals
طريقة.بالإضافة إلى ذلك، إذا كان كلاهماobjA
وobjB
نكونnull
, ، تعود الطريقةtrue
. - ويحدد ما إذا كان أي منهما
objA
أوobjB
يكونnull
.إذا كان الأمر كذلك، فإنه يعودfalse
.إذا كان الكائنان لا يمثلان نفس مرجع الكائن ولا هو كذلكnull
, ، يدعوobjA.Equals(objB)
وإرجاع النتيجة.وهذا يعني أنه إذاobjA
يتجاوزObject.Equals(Object)
الطريقة، ويسمى هذا التجاوز.
.
public static bool operator ==(Foo objA, Foo objB) {
return Object.Equals(objA, objB);
}
الرد أكثر على عامل التشغيل المتجاوز كيفية المقارنة بـ null التي تعيد التوجيه هنا كنسخة مكررة.
في الحالات التي يتم فيها ذلك لدعم كائنات القيمة، أجد أن الترميز الجديد مفيد، وأرغب في التأكد من وجود مكان واحد فقط حيث يتم إجراء المقارنة.كما أن الاستفادة من Object.Equals(A, B) تعمل على تبسيط عمليات التحقق من القيمة الفارغة.
سيؤدي هذا إلى التحميل الزائد على == و!= وEquals وGetHashCode
public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
public override bool Equals(object other) => Equals(other as ValueObject );
public bool Equals(ValueObject other) {
return !(other is null) &&
// Value comparisons
_value == other._value;
}
public override int GetHashCode() => _value.GetHashCode();
بالنسبة للكائنات الأكثر تعقيدًا، قم بإضافة مقارنات إضافية في Equals وGetHashCode الأكثر ثراءً.
هناك في الواقع طريقة أبسط للتحقق منها null
في هذه الحالة:
if (foo is null)
هذا كل شيء!
تم تقديم هذه الميزة في الإصدار C#7
خطأ شائع في التحميل الزائد للمشغل == هو الاستخدام
(a == b)
,(a ==null)
, ، أو(b == null)
للتحقق من المساواة المرجعية.هذا بدلا من ذلك النتائج في استدعاء عامل التشغيل المحمل بشكل زائد ==، مما يتسبب في حدوث خطأinfinite loop
.يستخدمReferenceEquals
أو يلقي النوع إلى الكائن ، لتجنب الحلقة.
ألق نظرة على هذا
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
return false;
}
يمكنك محاولة استخدام خاصية كائن والتقاط NullReferenceException الناتج.إذا كانت الخاصية التي تحاول تجربتها موروثة أو تم تجاوزها من الكائن، فهذا ينطبق على أي فئة.
public static bool operator ==(Foo foo1, Foo foo2)
{
// check if the left parameter is null
bool LeftNull = false;
try { Type temp = a_left.GetType(); }
catch { LeftNull = true; }
// check if the right parameter is null
bool RightNull = false;
try { Type temp = a_right.GetType(); }
catch { RightNull = true; }
// null checking results
if (LeftNull && RightNull) return true;
else if (LeftNull || RightNull) return false;
else return foo1.field1 == foo2.field2;
}