7

I am using C#. When I am comparing two char value its sending me correct output, like,

'-'.CompareTo('!') //Its sending me positive value 12

means '-' > '!' is true

But when I am comparing two string of same value its sending me different result

"-".CompareTo("!") //Its sending me negative value -1

means "-" > "!" is false

Can anyone please explain me why it is doing so ? Should not it be 'true' for both cases ?

Arnab
  • 404
  • 4
  • 11
  • 1
    `CompareTo` is used for sorting, not (necessarily) for checking equality. Instances are considered "equal" if `CompareTo` returns zero. See [Char.CompareTo](http://msdn.microsoft.com/library/bhh2bx3h.aspx) and [String.CompareTo](http://msdn.microsoft.com/library/35f0x18w.aspx) for further information. – Corak Jul 25 '14 at 07:56
  • From [Char.CompareTo](http://msdn.microsoft.com/library/bhh2bx3h.aspx) : The comparison performed by this method is based on the encoded values of this instance and *value*, not their lexicographical characteristics. – Corak Jul 25 '14 at 08:01
  • @Corak - I am implementing a binary search method for string. So I need a sorted array. So do you thing I should use CompareTo() or my own comparison method ? – Arnab Jul 25 '14 at 08:08
  • 2
    @Arnab be careful to do custom string comparison unless you're really aware of difference between cultures (or you work with a single culture). Character comparison (as you can see) it's pretty misleading. – Adriano Repetti Jul 25 '14 at 08:13
  • 1
    "Depends"... In any case, you probably don't want the `char` logic when comparing `strings`. The question is how *do* you want them sorted. Should it be culture aware, or culture independent, or lexicographically independent? Does `String.CompareTo` or `String.CompareToOrdinal` sort like you want? Then use those, otherwise build your own sorting logic. -- you might also want to take a look at [Compare with StringComparison](http://msdn.microsoft.com/library/e6883c06.aspx) and [Compare with CultureInfo](http://msdn.microsoft.com/library/cc190529.aspx) – Corak Jul 25 '14 at 08:16

3 Answers3

9

String's Compare method is culture specific. That's why you get different results. use string.CompareOrdinal instead, which is byte by byte comparison.

var v = '-'.CompareTo('!');//12
var s = string.CompareOrdinal("-", "!");//12

Best Practices for Using Strings in the .NET Framework

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • I am implementing a binary search method for string. So I need a sorted array. So do you thing using CompareTo() will be any problem or should I use CompareOrdinal ? – Arnab Jul 25 '14 at 08:12
  • That depends on how your results needs to be, If you need culture dependent comparison use `CompareTo`, There are other options to compare, they are `InvariantCulture` and ignore case variants of them. Refer to the link I provided and choose your best fit. – Sriram Sakthivel Jul 25 '14 at 08:16
4

This comparison '-'.CompareTo('!') will perform an ordinal comparison. It'll compare numeric UTF-16 encoded values (45 and 33).

String comparison "-".CompareTo("!") is different and it'll perform a culture aware comparison. It means that, no matters the numeric value, characters will be ordered according to sorting rules for current culture.

You can try yourself using ordinal comparison for strings:

String.CompareOrdinal("-", "!")

That will perform an ordinal comparison on strings and then you'll get same result (12).

You can't perform a (true) culture aware comparison on Char (in case you need it simply convert to string) because sorting order may be affected by characters before and/or after what you're comparing, single character may not be a grapheme (and ordering may not apply). One example: in Czech language C comes before H then you expect "ch".CompareTo("h") == -1...wrong, "ch" is a digraph and it's between H and I then "ch".CompareTo("h") == 1!!! More on this on this more detailed post.

Ordinal comparison is different simply because of heritage from ASCII (every culture I tried returned same result for that ordering). They preserved ASCII ordering for compatibility (and easier migration to Unicode) but for string comparison they must respect culture rules.

A more common example of this is with upper-case/lower-case characters (note ' and " to perform ordinal and culture-aware comparison):

'A'.CompareTo('a') != "A".CompareTo("a")

If you're doing this to perform a text search then I strongly suggest you do not use Char comparison directly unless you're aware of culture issues (ordering) and Unicode details (surrogates and encoding, primary).

Community
  • 1
  • 1
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
1

This is due to a difference in implementation of IComparable method CompareTo in Char and String classes

Char.cs

public int CompareTo(Char value) {
      return (m_value-value);
}

String.cs

public int CompareTo(String strB) {
    return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, 0);
}

where logic is culture-aware comparison which rely in the internal InternalCompareString.

Konrad Kokosa
  • 16,563
  • 2
  • 36
  • 58