2

Note: my case is for byte[] but I believe a good answer would work for any type.

Visual Studio's auto-generated implementation of Equals uses EqualityComparer<T>.Default.Equals(T x, T y) for reference types. I have a lot of classes with byte arrays that needs to be included in Equals so I'd like to keep Visual studio's code if possible but Default returns a ObjectEqualityComparer for byte arrays. I've written a simple byte array comparer but I'm not sure how to proceed to have it used instead of ObjectEqualityComparer.

public class Foo
{
    public int Id {get;set;}
    public byte[] Data {get;set;}

    public override bool Equals(object obj)
    {
        var foo = obj as Foo;
        return foo != null &&
            Id == foo.Id &&
            EqualityComparer<byte[]>.Default.Equals(Data, foo.Data);
    }
}

static void Main
{
    Foo f1 = new Foo { Id = 1, Data = new byte[1] { 0xFF } };
    Foo f2 = new Foo { Id = 1, Data = new byte[1] { 0xFF } };
    bool result = f1.Equals(f2); // false
}

public class ByteArrayComparer
{
    public bool Equals(byte[] x, byte[] y)
    {
        return x.SequenceEqual(y);
    }

    public int GetHashCode(byte[] obj)
    {
        return obj.GetHashCode(); 
        // as Servy said, this is wrong but it's not the point of the question, 
        // assume some working implementation
    }
}

Should ByteArrayComparer implement IEqualityComparer, inherit from EqualityComparer and override the methods, or something else?

0xFF
  • 808
  • 1
  • 12
  • 33
  • 2
    There is no way to get `EqualityComparer.Default` to return anything else no matter what you write. It'd be quite the global problem if you could -- whose "global default equality comparer for `byte[]`" is the right one? A custom *type* can implement `IEquatable`, but for the existing system types you don't get to assign new, "more sensible" defaults. The best you can do is create new comparers, and use those explicitly. – Jeroen Mostert Mar 26 '19 at 16:02
  • 1
    Your comparer doesn't return the same hash code for "equal" arrays, so it doesn't even work. Be thankful you aren't able to make that the default comparer. – Servy Mar 26 '19 at 16:05
  • @JeroenMostert Right, I figured it wasn't possible. – 0xFF Mar 26 '19 at 16:07
  • At least it should implement `IEqualityComparer` to use it e.g. in linq methods, but you will not be able to let `EqualityComparer.Default` return an instance of your class. – René Vogt Mar 26 '19 at 16:07
  • @Servy Oh yeah, I just filled whatever for the sample. – 0xFF Mar 26 '19 at 16:08
  • @0xFF Don't do that. If you don't want to provide an implementation, throw a not implemented exception or something, so at least it's clear it's not intended to work, rather than just returning an incorrect result and violating the contract of a comparer. – Servy Mar 26 '19 at 16:10
  • @Servy: throwing an exception from `GetHashCode()` isn't allowed either, so then we would have complained about *that*. :-P The simplest legal implementation is `GetHashCode() => 0` -- horribly inefficient, but "correct". – Jeroen Mostert Mar 26 '19 at 16:12
  • @JeroenMostert your first comment would be the answer, feel free to post it and I'll accept it. – 0xFF Mar 26 '19 at 16:13
  • @JeroenMostert Throwing an exception at least makes it clear that it's not designed to work, rather than behaving incorrectly, silently. Returning zero is worse still, because it'll effectively not work, but in a way that makes it very hard to figure out what's wrong. The whole problem here is writing code that won't work, but that isn't obvious to someone running into problems with it why it isn't working. – Servy Mar 26 '19 at 16:17
  • 1
    @Servy: thanks, you have eloquently elaborated the purpose of my quotes around "correct". – Jeroen Mostert Mar 26 '19 at 16:22
  • @JeroenMostert So then why are you advocating a behavior that you know well to be harmful, and just assuming that anyone reading your comments will know that because you used quotes around "correct" you actually meant, "entirely incorrect and something you should never do"? – Servy Mar 26 '19 at 17:30

1 Answers1

2

Create and use an instance of your custom comparer instead of using EqualityComparer<byte[]>.Default in your class:

public class Foo
{
    public int Id { get; set; }
    public byte[] Data { get; set; }

    private readonly ByteArrayComparer _comparer = new ByteArrayComparer();

    public override bool Equals(object obj)
    {
        var foo = obj as Foo;
        return foo != null &&
            Id == foo.Id &&
            _comparer.Equals(Data, foo.Data);
    }
}

You may also want to implement IEqualityComparer<T> and GetHashCode() in your ByteArrayComparer class. EqualityComparer<T>.Default returns an instance of a class that implements this interface, but I assume you don't want to use this one as you have implemented your own custom comparer.

How to use the IEqualityComparer

mm8
  • 163,881
  • 10
  • 57
  • 88