كيف يمكنني التحقق من وجود القيم الخالية في التحميل الزائد لعامل التشغيل '==' دون تكرار لا نهائي؟

StackOverflow https://stackoverflow.com/questions/73713

  •  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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top