Search code examples
.net.net-3.5equalsiequalitycomparer

EqualityComparer<Uri>.Default.Equals() returning wrong result or what?


Is there an explanation for this other than being a bug in the .NET Framework? The EqualityComparer<Uri>.Default.Equals() method is saying that the following URLs are equal!

http://books.google.com/books?id=B84KAQAAIAAJ&pg=PA29&lpg=PA29&dq=fletcher+sandford+tilton&source=bl&ots=ou8eF5REOG&sig=74fzA11Z8AENBtyCUcXEsXV06jQ&hl=en&ei=2rHTS9LaN4249gTOh_GrDw&sa=X&oi=book_result&ct=result&resnum=3&ved=0CA0Q6AEwAg#v=onepage&q=fletcher s

and

http://books.google.com/books?id=B84KAQAAIAAJ&pg=PA29&lpg=PA29&dq=fletcher+sandford+tilton&source=bl&ots=ou8eF5REOG&sig=74fzA11Z8AENBtyCUcXEsXV06jQ&hl=en&ei=2rHTS9LaN4249gTOh_GrDw&sa=X&oi=book_result&ct=result&resnum=3&ved=0CA0Q6AEwAg#v=onepage&q=fletcher

Notice the space followed by 's' at the end of the first one.


Solution

  • Well, the concern (whether right or wrong) isn't in EqualityComparer<Uri>.Default. It calls into Uri.Equals() as it should.

    Now, Uri.Equals() ignores differences on fragment alone. In a great many cases that is appropriate. In a great many it's not. Personally I wouldn't have had it as the default, but then since I'm not someone who coded it perhaps I don't know of some compelling reasons to have things as they are.

    Note that this is documented.

    Other decisions are also debatable (that it ignores case-difference on host components matches many practical concerns about URIs, but not how URI equality is defined in some specifications).

    If you need a stricter equality than that, I suggest you define your own comparator:

    public class UriStictEqualityComparer : IEqualityComparer<Uri>
    {
      public bool Equals(Uri x, Uri y)
      {
        return ReferenceEquals(x, y)
          ||
          (
            x != null
            &&
            y != null
            &&
            x.IsAbsoluteUri == y.IsAbsoluteUri
            &&
            x.ToString() == y.ToString()
          );
      }
      public int GetHashCode(Uri obj)
      {
        return obj == null ? 0 : obj.ToString().GetHashCode();
      }
    }
    

    Still, you may find that you want some cases the above considers unequal, to be equal too. E.g. you need to consider whether punycode and non-punycode versions are the same, whether escaped non-special characters should be unescaped, and so on. Uri's Compare method can be a benefit in such cases.