When comparing two strings in c# for equality, what is the difference between InvariantCulture and Ordinal comparison?
-
3For those using `String1.Equals(String2, StringComparison.Ordinal)`, you better use `String1 == String2` which is intrinsically `String1.Equals(String2)` and it is by default an ordinal case-sensitive comparison. – Ghasan غسان Nov 05 '14 at 12:53
-
3@Ghasan Not sure if that makes `==` "better", but it is a) shorter, b) less explicit about what exactly it does and c) `String1` can be null without the comparison throwing a `NullReferenceException`. – Evgeniy Berezovsky Jul 28 '15 at 04:18
-
4@Ghasan the official MSDN *Best Practices for Using Strings in the .NET Framework* page (https://msdn.microsoft.com/en-us/library/dd465121(v=vs.110).aspx#specifying_string_comparisons_explicitly) recommends the usage of overloads that explicitly specify the `StringComparison` type. In the case of string comparison, it means `String.Equals`. – Ohad Schneider Aug 12 '16 at 11:00
-
3@EugeneBeresovsky To avoid `NullReferenceException` you can simply use the static method: `String.Equals(string1, string2, StringComparison.Ordinal)`. – Ohad Schneider Aug 12 '16 at 11:01
-
Maybe http://www.siao2.com/2004/12/29/344136.aspx ? (googled) – Cheese Daneish Jan 29 '09 at 18:30
9 Answers
InvariantCulture
Uses a "standard" set of character orderings (a,b,c, ... etc.). This is in contrast to some specific locales, which may sort characters in different orders ('a-with-acute' may be before or after 'a', depending on the locale, and so on).
Ordinal
On the other hand, looks purely at the values of the raw byte(s) that represent the character.
There's a great sample at http://msdn.microsoft.com/en-us/library/e6883c06.aspx that shows the results of the various StringComparison values. All the way at the end, it shows (excerpted):
StringComparison.InvariantCulture:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is less than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)
StringComparison.Ordinal:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is greater than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)
You can see that where InvariantCulture yields (U+0069, U+0049, U+00131), Ordinal yields (U+0049, U+0069, U+00131).

- 27,717
- 28
- 128
- 190

- 6,955
- 1
- 22
- 21
-
34
-
186I feel like is useful information, but does not actually answer the question. When determining *Equality* of two strings, is there any reason to use InvarintCulture instead of Ordinal? It seems that InvariantCulture would be used to *Sort* strings, and Ordinal should be used for *Equality* checking (we don't care that accented-a comes before or after a, it's simply different). Though, I myself am a little unsure of this point. – MPavlak Mar 21 '12 at 16:32
-
21See http://msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx and notice that string normalization and ordinal comparison is recommended. – MPavlak Mar 21 '12 at 16:54
-
30
-
2@Joey seeing as .NET uses the same encoding for all strings (UTF-16) what's the difference between code points and bytes? Also the docs state *"Specifying the StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase value in a method call signifies a non-linguistic comparison in which the features of natural languages are ignored. Methods that are invoked with these StringComparison values base string operation decisions on simple byte comparisons"* http://msdn.microsoft.com/en-us/library/dd465121(v=vs.100).aspx – Ohad Schneider May 22 '14 at 11:16
-
11There is good performance test results published [C# String Comparision Tests](http://rhale78.wordpress.com/2011/05/16/string-equality-and-performance-in-c/) which tells the performance of each different string comparision methods and their time. – Kumar C Dec 13 '14 at 22:44
-
[Official MSDN link for explaining the difference](https://msdn.microsoft.com/library/bb385972.aspx) – user3613932 Aug 17 '17 at 22:17
-
4Concerning MPavlak's "unsure" comment above with (at this time) 106 upvotes, suggesting there is no difference between Ordinal and Invariant for equality checks, there actually is. They are different even for equality checks because of character expansion, as evidenced in another answer in this same thread: https://stackoverflow.com/a/20085219/88409 – Triynko Aug 13 '18 at 22:13
-
2
-
It does matter, for example - there is a thing called character expansion
var s1 = "Strasse";
var s2 = "Straße";
s1.Equals(s2, StringComparison.Ordinal); //false
s1.Equals(s2, StringComparison.InvariantCulture); //true
With InvariantCulture
the ß character gets expanded to ss.

- 56,041
- 24
- 146
- 247

- 6,882
- 1
- 25
- 28
-
1Does this thing also differ in some way between `Ordinal` and `InvariantCulture`? That's what the original question is about. – Matthijs Wessels Feb 20 '14 at 12:54
-
That's the only difference I have found so far when looking for differences in equality matching. Everything else is about character ordering. – CRice Aug 20 '14 at 01:56
-
7For those that don't know `ß` it should be noted that `ß` at least in german equals to a double s, Source: https://en.wikipedia.org/wiki/%C3%9F – Peter Mar 02 '16 at 07:46
-
32That's not quite correct @Peter, you cannot use `ß` and `ss` interchangeably in German (I'm a native speaker). There are cases where both are legal (but often one is outdated / not recommended) and there are cases where only one form is allowed. – enzi Mar 22 '16 at 17:22
-
9This simple example clearly demonstrates the difference between the 2 comparisons. I think I'm getting this now. – BrianLegg Feb 20 '17 at 16:51
-
6Had to try it: https://ideone.com/j8DvDo so cool! A little lesson in German as well. Wondering what's the difference between ß and ss now... – Lzh May 10 '18 at 00:58
-
2
-
26
-
4See related article here: [Behavior changes when comparing strings on .NET 5+](https://learn.microsoft.com/en-us/dotnet/standard/base-types/string-comparison-net-5-plus). – Olivier Jacot-Descombes Jun 30 '22 at 13:36
Pointing to Best Practices for Using Strings in the .NET Framework:
- Use
StringComparison.Ordinal
orStringComparison.OrdinalIgnoreCase
for comparisons as your safe default for culture-agnostic string matching. - Use comparisons with
StringComparison.Ordinal
orStringComparison.OrdinalIgnoreCase
for better performance. - Use the non-linguistic
StringComparison.Ordinal
orStringComparison.OrdinalIgnoreCase
values instead of string operations based onCultureInfo.InvariantCulture
when the comparison is linguistically irrelevant (symbolic, for example).
And finally:
- Do not use string operations based on
StringComparison.InvariantCulture
in most cases. One of the few exceptions is when you are persisting linguistically meaningful but culturally agnostic data.

- 15,573
- 9
- 52
- 68
Another handy difference (in English where accents are uncommon) is that an InvariantCulture comparison compares the entire strings by case-insensitive first, and then if necessary (and requested) distinguishes by case after first comparing only on the distinct letters. (You can also do a case-insensitive comparison, of course, which won't distinguish by case.) Corrected: Accented letters are considered to be another flavor of the same letters and the string is compared first ignoring accents and then accounting for them if the general letters all match (much as with differing case except not ultimately ignored in a case-insensitive compare). This groups accented versions of the otherwise same word near each other instead of completely separate at the first accent difference. This is the sort order you would typically find in a dictionary, with capitalized words appearing right next to their lowercase equivalents, and accented letters being near the corresponding unaccented letter.
An ordinal comparison compares strictly on the numeric character values, stopping at the first difference. This sorts capitalized letters completely separate from the lowercase letters (and accented letters presumably separate from those), so capitalized words would sort nowhere near their lowercase equivalents.
InvariantCulture also considers capitals to be greater than lower case, whereas Ordinal considers capitals to be less than lowercase (a holdover of ASCII from the old days before computers had lowercase letters, the uppercase letters were allocated first and thus had lower values than the lowercase letters added later).
For example, by Ordinal: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"
And by InvariantCulture: "0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"

- 1,654
- 12
- 36

- 4,078
- 1
- 25
- 26
-
I took another look at this and noticed an inconsistency between the InvariantCulture example and my explanation on the handling of accented characters. The example appears to be correct, so I've corrected the explanation to be consistent. The InvariantCulture comparison does not stop at the first differing accent and appears to only consider an accent difference on the same letter if the rest of the strings match besides accents and case. An accent difference is then considered before an earlier case difference, so "Aaba" < "aába". – Rob Parker Jan 18 '13 at 01:03
Although the question is about equality, for quick visual reference, here the order of some strings sorted using a couple of cultures illustrating some of the idiosyncrasies out there.
Ordinal 0 9 A Ab a aB aa ab ss Ä Äb ß ä äb ぁ あ ァ ア 亜 A
IgnoreCase 0 9 a A aa ab Ab aB ss ä Ä äb Äb ß ぁ あ ァ ア 亜 A
--------------------------------------------------------------------
InvariantCulture 0 9 a A A ä Ä aa ab aB Ab äb Äb ss ß ァ ぁ ア あ 亜
IgnoreCase 0 9 A a A Ä ä aa Ab aB ab Äb äb ß ss ァ ぁ ア あ 亜
--------------------------------------------------------------------
da-DK 0 9 a A A ab aB Ab ss ß ä Ä äb Äb aa ァ ぁ ア あ 亜
IgnoreCase 0 9 A a A Ab aB ab ß ss Ä ä Äb äb aa ァ ぁ ア あ 亜
--------------------------------------------------------------------
de-DE 0 9 a A A ä Ä aa ab aB Ab äb Äb ß ss ァ ぁ ア あ 亜
IgnoreCase 0 9 A a A Ä ä aa Ab aB ab Äb äb ss ß ァ ぁ ア あ 亜
--------------------------------------------------------------------
en-US 0 9 a A A ä Ä aa ab aB Ab äb Äb ß ss ァ ぁ ア あ 亜
IgnoreCase 0 9 A a A Ä ä aa Ab aB ab Äb äb ss ß ァ ぁ ア あ 亜
--------------------------------------------------------------------
ja-JP 0 9 a A A ä Ä aa ab aB Ab äb Äb ß ss ァ ぁ ア あ 亜
IgnoreCase 0 9 A a A Ä ä aa Ab aB ab Äb äb ss ß ァ ぁ ア あ 亜
Observations:
de-DE
,ja-JP
, anden-US
sort the same wayInvariant
only sortsss
andß
differently from the above three culturesda-DK
sorts quite differently- the
IgnoreCase
flag matters for all sampled cultures
The code used to generate above table:
var l = new List<string>
{ "0", "9", "A", "Ab", "a", "aB", "aa", "ab", "ss", "ß",
"Ä", "Äb", "ä", "äb", "あ", "ぁ", "ア", "ァ", "A", "亜" };
foreach (var comparer in new[]
{
StringComparer.Ordinal,
StringComparer.OrdinalIgnoreCase,
StringComparer.InvariantCulture,
StringComparer.InvariantCultureIgnoreCase,
StringComparer.Create(new CultureInfo("da-DK"), false),
StringComparer.Create(new CultureInfo("da-DK"), true),
StringComparer.Create(new CultureInfo("de-DE"), false),
StringComparer.Create(new CultureInfo("de-DE"), true),
StringComparer.Create(new CultureInfo("en-US"), false),
StringComparer.Create(new CultureInfo("en-US"), true),
StringComparer.Create(new CultureInfo("ja-JP"), false),
StringComparer.Create(new CultureInfo("ja-JP"), true),
})
{
l.Sort(comparer);
Console.WriteLine(string.Join(" ", l));
}

- 18,571
- 13
- 82
- 156
-
2Hmmm - OK, it's nice that you did this research, and posted your findings, although I'm not exactly sure what your point is. Anyway, Danish may not be one of the "most important cultures" (although 5 million Danes are actually rather fond of their culture), but if you throw "aa" in as an additional test string, and "da-DK" in as an additional test culture, you'll see some interesting results. – RenniePet Jun 11 '14 at 00:09
-
2@RenniePet Thanks for that. I added Danish, as it sorts quite differently than the 3 other cultures used. (As emoticons indicating irony don't seem to be as well-understood in the English language reading web as I would have assumed, I removed the "most important cultures" comment. After all, the BCL does not feature a `CultureComparer` which we could use to verify. For this table, the `Danish` culture(info) turned out to be very important.) – Evgeniy Berezovsky Jun 11 '14 at 00:39
-
1Thanks. I did realize that your "most important cultures" comment was intended to be taken with a grain of salt - it's just that I've gotten too old to use emoticons. I figure that texting has become so common that using emoticons is sort of like explaining your jokes after you tell them, irrespective of whether or not anyone laughs. Incidentally, the other Scandinavian cultures (Finnish, Norwegian and Swedish) are the same as Danish, except for the very special handling of "aa" - which proves that Danish is the superior culture, of course. – RenniePet Jun 11 '14 at 01:03
-
1For what it's worth, Danish sorts ä and aa differently because of the location of the special letters æ (ae), ø (oe, ö), and å (aa, ä) at the end of the alphabet in the written order. – Alrekr Jun 04 '18 at 08:34
Invariant is a linguistically appropriate type of comparison.
Ordinal is a binary type of comparison. (faster)
See http://www.siao2.com/2004/12/29/344136.aspx
Here is an example where string equality comparison using InvariantCultureIgnoreCase and OrdinalIgnoreCase will not give the same results:
string str = "\xC4"; //A with umlaut, Ä
string A = str.Normalize(NormalizationForm.FormC);
//Length is 1, this will contain the single A with umlaut character (Ä)
string B = str.Normalize(NormalizationForm.FormD);
//Length is 2, this will contain an uppercase A followed by an umlaut combining character
bool equals1 = A.Equals(B, StringComparison.OrdinalIgnoreCase);
bool equals2 = A.Equals(B, StringComparison.InvariantCultureIgnoreCase);
If you run this, equals1 will be false, and equals2 will be true.

- 618
- 5
- 11
-
Just to add another similar example but with string literals, if `a="\x00e9"` (e acute) and `b="\x0065\x0301"` (e combined with an acute accent), `StringComparer.Ordinal.Equals(a, b)` will return false while `StringComparer.InvariantCulture.Equals(a, b)` will return true. – George Helyar Jan 11 '18 at 17:44
No need to use fancy unicode char exmaples to show the difference. Here's one simple example I found out today which is surprising, consisting of only ASCII characters.
According to the ASCII table, 0
(48, 0x30) is smaller than _
(95, 0x5F) when compared ordinally. InvariantCulture would say the opposite (PowerShell code below):
PS> [System.StringComparer]::Ordinal.Compare("_", "0")
47
PS> [System.StringComparer]::InvariantCulture.Compare("_", "0")
-1
Always try to use InvariantCulture in those string methods that accept it as overload. By using InvariantCulture you are on a safe side. Many .NET programmers may not use this functionality but if your software will be used by different cultures, InvariantCulture is an extremely handy feature.

- 161
- 1
- 2
-
6If your software will not be used by different cultures, it is much slower than Ordinal though. – Kyle Aug 07 '12 at 23:51