AssetLabelReference not equivalence-testable out of the box

Take a look at the following test. Which method do you think fails?

public class AddressableTests
{
    readonly AssetLabelReference thing = new AssetLabelReference() {labelString = "thing"};
    readonly AssetLabelReference sameThing = new AssetLabelReference() {labelString = "thing"};
    readonly AssetLabelReference other = new AssetLabelReference() {labelString = "other"};
    
    [Test]
    public void AssetLabelEqualsMethod()
    {
        Assert.IsTrue(thing.Equals(sameThing));
        Assert.IsFalse(thing.Equals(other));
    }

    [Test]
    public void AssetLabelEqualsOperator()
    {
        Assert.IsTrue(thing == sameThing);
        Assert.IsFalse(thing == other);
    }
}

They both fail, sadly. AssetLabelReference overrides GetHashCode, but it doesn’t implement an Equals method, so equivalence is tested through ReferenceEquals as usual (this surprised me, too). This could cause unexpected behavior when using AssetLabelReferences as dictionary keys or in hash sets.

I recommend implementing IEquatable so that we don’t have to explicitly reference the stringLabel inside to test equivalency.

Somehow, I managed to forget about the equality test issue and hit this issue again. This is larger a pain point than I expected.

        var searchLabelsSet = new HashSet<AssetLabelReference>(search);
        var requireLabelsSet = new HashSet<AssetLabelReference>(require);
        var ignoreLabelsSet = new HashSet<AssetLabelReference>(ignore);
        Debug.Log("Removing: " + string.Join('|', requireLabelsSet.Select(lbl => lbl.labelString)));
        searchLabelsSet.ExceptWith(requireLabelsSet);
        Debug.Log("Removing: " + string.Join('|', ignoreLabelsSet.Select(lbl => lbl.labelString)));
        searchLabelsSet.ExceptWith(ignoreLabelsSet);
        Debug.Log("Final search label list: " + string.Join('|', searchLabelsSet.Select(lbl => lbl.labelString)));

The “final search label list” here is not cleaned of the other two sets because AssetLabelReferences do not implement IEquatable.

For the time being, the following EqualityComparer can be supplied to hash sets and dictionaries to correctly test label equality.

public class AssetLabelReferenceComparer : IEqualityComparer<AssetLabelReference>
{
    public bool Equals(AssetLabelReference x, AssetLabelReference y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        return x.labelString.Equals(y.labelString);
    }

    public int GetHashCode(AssetLabelReference obj)
    {
        return obj.GetHashCode();
    }
}
1 Like