【问题标题】:Trouble with generics using custom equality comparison [duplicate]使用自定义相等比较的泛型问题[重复]
【发布时间】:2020-05-05 04:26:36
【问题描述】:

我的班级层次结构如下:

public abstract class Issue
{
    // some base properties
    // these are not included in equality checks for deriving classes
    // also not performing equality checks on this base class (it is abstract anyway)
}

public class IssueTypeA : Issue, IEquatable<IssueTypeA>
{
    // some properties specific to this class
    // these are the two for which equality comparison is performed
    public string requirement { get; set; }
    public string preamble { get; set; }

    public bool Equals(IssueTypeA that)
    {
        // determine based on the values of the properties
        // they must both be the same for equality
        if ((this.requirement.Equals(that.requirement)) &&
           (this.preamble.Equals(that.preamble)))
        {
            return true;
        }

        return false;
    }
}

public class IssueTypeB : Issue, IEquatable<IssueTypeB>
{
    // some properties specific to this class

    public bool Equals(IssueTypeB that)
    {
        // determine based on the values of the properties
    }
}

还有一个类,目的是接收上述层次结构中的对象(当然基类除外),并与它们做一些比较操作:

public class Comparer<T> where T : Issue
{
    // various comparison methods
    public IEnumerable<T> getReferenceChangedIssues(IEnumerable<T> spreadsheetIssues, IEnumerable<T> downloadedIssues)
    {
        foreach (T spreadsheetRecord in T spreadsheetIssues)
        {
            foreach (T downloadedIssue in downloadedIssues)
            {
                // this is the point of failure
                // there are cases in which this should be true, but it is not
                if (spreadsheetRecord.Equals(downloadedIssue))
                {
                    // the referenceChanged method works fine by itself
                    // it has been unit tested
                    if (spreadsheetRecord.referenceChanged(downloadedIssue))
                        yield return spreadsheetRecord;
                }
            }
        }
    }
}

通过单元测试和调试,很明显Comparer 没有正确使用上面定义的custom Equals 方法。为什么是这样?它还打算在未来包含一些具有一些集合操作的方法,这将需要Equals

【问题讨论】:

  • 如果比较你得到一个IssueTypeA和一个IssueTypeB,它会调用哪个Equals方法?
  • @YuriyFaktorovich 我没有尝试过。我会这样做,但在正常情况下,Issue 的两种不同子类型不会混合在一起。也许泛型在这里不适合。
  • 如果你实现了 IEquatable,你还应该重写 Object.EqualsObject.GetHashCodeComparer 包括哪些方法?它是否实现接口IComparer?你在Comparer的方法中遇到了什么问题?
  • @IliarTurdushev 我将添加一个包含在Comparer 中的方法示例。
  • @Al2110 现在我看到了Comparer 类的定义中的问题。当您调用spreadsheetRecord.Equals(downloadedIssue) 时,会调用方法Object.Equals(而不是IEquatable.Equals),因为Comparer 的类型T 不会被限制为实现IEquatable。将Comparer 的定义更改为下一个:class Comparer&lt;T&gt; where T : Issue, IEquatable&lt;T&gt;。还可以考虑在 Issue 类中覆盖 Object.EqualsObject.GetHashCode

标签: c# .net generics


【解决方案1】:

当您实现 IEquatable 时,您确实需要覆盖 bool Equals(object)GetHashCode。如果你不这样做,事情就会以奇怪的方式破裂。这是我能想到的最精简的东西。我认为它应该可以解决您的问题(但如果没有您的比较代码,我真的无法判断)。如果有人指出问题,我会修复代码:

public abstract class Issue : IEquatable<Issue>
{
    // possibly (not not necessarily) some base properties
    // if they are included, make them part of the equality comparison and
    // include them in the HashCode calculations

    public bool Equals(Issue other)
    {
        if (other == null)
        {
            return false;
        }
        if (this.GetType() != other.GetType())
        {
            return false;
        }
        //test local properties and return true/false based on their values
        return true;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Issue) obj);
    }

}

public class IssueTypeA : Issue, IEquatable<IssueTypeA>
{
    public string AName { get; set; } = "My A Name";
    public int AValue { get; set; } = 42;

    public bool Equals(IssueTypeA other)
    {
        if (!base.Equals(other))
        {
            return false;
        }

        return AName == other.AName && AValue == other.AValue;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((IssueTypeA) obj);
    }


    //hash-codes need to be consistent once they are calculated - jump through some hoops
    private int? _hashCodeValue = default;
    public override int GetHashCode()
    {
        if (!_hashCodeValue.HasValue)
        {
            _hashCodeValue = $"{AName}{AValue}".GetHashCode();
        }

        return _hashCodeValue.Value;
    }
}

public class IssueTypeB : Issue, IEquatable<IssueTypeB>
{
    // some properties specific to this class
    public string BName { get; set; } = "My B Name";
    public int BValue { get; set; } = 84;



    public bool Equals(IssueTypeB other)
    {
        if (!base.Equals(other))
        {
            return false;
        }
        //calculate equality based on local properties and return true/false
        return BName == other.BName && BValue == other.BValue;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((IssueTypeB) obj);
    }

    //hash-codes need to be consistent once they are calculated - jump through some hoops
    private int? _hashCodeValue = default;
    public override int GetHashCode()
    {
        if (!_hashCodeValue.HasValue)
        {
            _hashCodeValue = $"{BName}{BValue}".GetHashCode();
        }

        return _hashCodeValue.Value;
    }
}

与第一次相比,这是一个更好的解决方案。我根据每个类的本地属性检查是否相等。我还提供了一个一致的 GetHashCode 实现(以我认为 GetHashCode 需要的奇怪方式 - 但以一种让 Resharper 发疯的方式)。

【讨论】:

  • 我在原始帖子中添加了 IssueTypeA 的相等比较所基于的属性。我看到您在回答中举了一个示例,说明在比较中使用了基本属性。就我而言,他们不是。我还需要基类中的equals和get hash code方法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 2012-11-11
  • 2011-03-20
  • 2021-03-16
  • 2023-03-19
  • 2013-03-25
相关资源
最近更新 更多