كيف يمكنني تنفيذ IEqualityComparer على بنية زوجية عامة غير قابلة للتغيير؟

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

  •  02-07-2019
  •  | 
  •  

سؤال

لدي هذا حاليًا (تم تحريره بعد قراءة النصائح):

struct Pair<T, K> : IEqualityComparer<Pair<T, K>>
{
    readonly private T _first;
    readonly private K _second;

    public Pair(T first, K second)
    {
        _first = first;
        _second = second;

    }

    public T First { get { return _first; } }
    public K Second { get { return _second; } }

    #region IEqualityComparer<Pair<T,K>> Members

    public bool Equals(Pair<T, K> x, Pair<T, K> y)
    {
        return x.GetHashCode(x) == y.GetHashCode(y);
    }

    public int GetHashCode(Pair<T, K> obj)
    {
        int hashCode = obj.First == null ? 0 : obj._first.GetHashCode();

        hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode();

        return hashCode;
    }

    #endregion

    public override int GetHashCode()
    {
        return this.GetHashCode(this);
    }

    public override bool Equals(object obj)
    {
        return (obj != null) && 
    (obj is Pair<T, K>) && 
    this.Equals(this, (Pair<T, K>) obj);
    }
}

المشكلة هي أن الأول والثاني قد لا يكونان من الأنواع المرجعية (في الواقع يحذرني VS من هذا)، لكن الكود لا يزال يتم تجميعه.هل يجب أن ألقيهما (الأول والثاني) على كائنات قبل مقارنتهما، أم أن هناك طريقة أفضل للقيام بذلك؟

يحرر:لاحظ أنني يريد هذه البنية لدعم أنواع القيمة والمراجع (بمعنى آخر، التقييد حسب الفئة ليس حلاً صالحًا)

تحرير 2:بالنسبة إلى ما أحاول تحقيقه، أريد أن يعمل هذا في القاموس.ثانيًا، إن برنامج SRP ليس مهمًا بالنسبة لي الآن لأن هذا ليس جوهر هذه المشكلة حقًا - حيث يمكن دائمًا إعادة هيكلته لاحقًا.ثالثًا، لن تعمل المقارنة مع default(T) بدلاً من المقارنة مع null - جربها.

هل كانت مفيدة؟

المحلول

يبدو أنك بحاجة إلى IEquatable بدلاً من ذلك:

internal struct Pair<T, K> : IEquatable<Pair<T, K>>
{
  private readonly T _first;
  private readonly K _second;

  public Pair(T first, K second)
  {
    _first = first;
    _second = second;
  }

  public T First
  {
    get { return _first; }
  }

  public K Second
  {
    get { return _second; }
  }

  public bool Equals(Pair<T, K> obj)
  {
    return Equals(obj._first, _first) && Equals(obj._second, _second);
  }

  public override bool Equals(object obj)
  {
    return obj is Pair<T, K> && Equals((Pair<T, K>) obj);
  }

  public override int GetHashCode()
  {
    unchecked
    {
      return (_first != null ? _first.GetHashCode() * 397 : 0) ^ (_second != null ? _second.GetHashCode() : 0);
    }
  }
}

نصائح أخرى

يجب أن يكون تطبيق IEqualityComparer فئة مختلفة (وبالتأكيد ليس بنية كما تريد إعادة استخدام المرجع).

أيضًا، لا ينبغي أبدًا تخزين رمز التجزئة الخاص بك مؤقتًا، حيث أن تطبيق GetHashcode الافتراضي للبنية (الذي لا تتجاوزه) سيأخذ هذا العضو في الاعتبار.

إذا كنت تستخدم رموز التجزئة في مقارنة الطرق، فيجب عليك التحقق من "القيمة الحقيقية" إذا كانت رموز التجزئة متماثلة.

bool result = ( x._hashCode == y._hashCode );
if ( result ) { result = ( x._first == y._first && x._second == y._second ); }
// OR?: if ( result ) { result = object.Equals( x._first, y._first ) && object.Equals( x._second, y._second ); }
// OR?: if ( result ) { result = object.ReferenceEquals( x._first, y._first ) && object.Equals( x._second, y._second ); }
return result;

ولكن هناك مشكلة صغيرة في مقارنة الحقلين "_first" و"_ Second".افتراضيًا، تستخدم الأنواع المرجعية مبدأ المساواة مقارنة بطريقة "object.ReferenceEquals"، ويمكنها تجاوزها.لذا فإن الحل الصحيح يعتمد على "ما يجب فعله بالضبط" في طريقة المقارنة الخاصة بك.هل يجب استخدام طريقة "يساوي" للحقول "_first" و"_ Second" أو object.ReferenceEquals ؟أو شيء أكثر تعقيدا؟

فيما يتعلق بالتحذير، يمكنك استخدام الافتراضي (T) والافتراضي (K) بدلا من null.

لا أستطيع أن أرى ما تحاول تحقيقه، ولكن لا ينبغي أن تستخدم رمز التجزئة للمقارنة من أجل المساواة - ليس هناك ما يضمن أن كائنين مختلفين لن يكون لهما نفس رمز التجزئة.أيضًا، على الرغم من أن بنيتك غير قابلة للتغيير، فإن العضوين _الأول و_الثاني ليسا كذلك.

بادئ ذي بدء، ينتهك هذا الرمز مبدأ SRP.فئة الزوج تستخدم لعقد أزواج إذا كانت العناصر، أليس كذلك؟من غير الصحيح تفويض المساواة مقارنة الوظائف بها.

بعد ذلك، دعنا نلقي نظرة على الكود الخاص بك:

ستفشل طريقة Equals إذا كانت إحدى الوسيطات فارغة - ليست جيدة.يستخدم Equals رمز التجزئة لفئة Pair، ولكن ألق نظرة على تعريف GetHashCode، فهو مجرد مزيج من رموز التجزئة لأعضاء الزوج - ولا علاقة له بالمساواة في العناصر.أتوقع أن تقوم طريقة Equals بمقارنة البيانات الفعلية.أنا مشغول جدًا في الوقت الحالي ولا أستطيع تقديم التنفيذ الصحيح، للأسف.ولكن من النظرة الأولى، يبدو أن التعليمات البرمجية الخاصة بك خاطئة.سيكون من الأفضل أن تقدم لنا وصفًا لما تريد تحقيقه.أنا متأكد من أن أعضاء SO سيكونون قادرين على تقديم بعض النصائح لك.

هل يمكنني أن أقترح استخدام تعبيرات Lambda كمعلمة؟سيسمح لك هذا بتحديد كيفية مقارنة الأنواع العامة الداخلية.

لا أحصل على أي تحذير عند التجميع حول هذا ولكن أفترض أنك تتحدث عن مقارنة == فارغة؟يبدو أن فريق الممثلين سيجعل كل هذا أكثر نظافة إلى حد ما، نعم.

ملاحظة.يجب عليك حقًا استخدام فئة منفصلة للمقارنة.هذه الفئة التي تملأ دورين (كونها زوجًا ومقارنة أزواجها) هي فئة قبيحة تمامًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top