Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

September 23, 2009 / by Zsolt Soczó

INVARIANT vs. ORDINAL újra

A korábban írt cikkhez kapcsolódó anyag, ebből már jobban átjön a különbség:
New Recommendations for Using Strings in Microsoft .NET 2.0.

* DO: Use StringComparison.Ordinal or OrdinalIgnoreCase for comparisons as your safe default for culture-agnostic string matching.
* DO: Use StringComparison.Ordinal and OrdinalIgnoreCase comparisons for increased speed.
* DO: Use StringComparison.CurrentCulture-based string operations when displaying the output to the user.
* DO: Switch current use of string operations based on the invariant culture to use the non-linguistic StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when the comparison is linguistically irrelevant (symbolic, for example).
* DO: Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison.
* DON’T: Use overloads for string operations that don’t explicitly or implicitly specify the string comparison mechanism.
* DON’T: Use StringComparison.InvariantCulture-based string operations in most cases; one of the few exceptions would be persisting linguistically meaningful but culturally-agnostic data.

Ebből az jön le, hogy használd vagy az ordinalt, vagy a “kulturált” megoldást, de ha lehet ne az invariantot.

# Ordinal comparisons are string comparisons in which each byte of each string is compared without linguistic interpretation. This is essentially a C runtime strcmp. Thus, “windows” would not match “Windows.” Where the context dictates that strings should be matched exactly, or demands conservative matching policy, this comparison should be used. Additionally, ordinal comparisons are the fastest because they apply no linguistic rules when determining a result.

# Case insensitive ordinal comparisons are the next most conservative, and ignore most casing. Thus, “windows” would match “Windows.” When dealing with ASCII characters, this policy is equivalent to that of StringComparison.Ordinal, but with the usual ASCII casing ignored. Thus, any character in [A, Z] (\u0041-\u005A) matches the corresponding one in [a,z] (\u0061-\007A). Casing outside the ASCII range uses the invariant culture’s tables…

Oridinal, case sensitive bináris összehasonlítás, gyors, ok.
Oridinal, case insensitive ascii karakterek esetén egyenlőnek tekinti a kis-nagybetűket, más karakterek esetén átvált az invariantra. Praktikusan szerintem ez azt jelenti, hogy ordinal case insensitive == invariant case insensitive.
Update: Jogos az előbbi, mert: “Comparisons made using OrdinalIgnoreCase are behaviorally the composition of two calls: calling ToUpperInvariant on both string arguments, and doing an Ordinal comparison.”

Közelebbről:

using System;

class Program
{
static void Main(string[] args)
{
string kisalma = “alma”;
string nagyalma = “Alma”;
Compare(kisalma, nagyalma);
Console.WriteLine();
kisalma = “őlma”;
nagyalma = “Őlma”;
Compare(kisalma, nagyalma);
}

private static void Compare(string a, string b)
{
Test(a, b, StringComparison.CurrentCulture);
Test(a, b, StringComparison.CurrentCultureIgnoreCase);
Test(a, b, StringComparison.InvariantCulture);
Test(a, b, StringComparison.InvariantCultureIgnoreCase);
Test(a, b, StringComparison.Ordinal);
Test(a, b, StringComparison.OrdinalIgnoreCase);
}

private static void Test(string a, string b, StringComparison c)
{
int compRes = string.Compare(a, b, c);

Console.WriteLine(“{0}, {1} {2} {3}”, c, a,
compRes == 0 ? “==” : (compRes < 0 ? "<": ">“),
b);
}
}

CurrentCulture, alma < Alma
CurrentCultureIgnoreCase, alma == Alma
InvariantCulture, alma < Alma
InvariantCultureIgnoreCase, alma == Alma
Ordinal, alma > Alma
OrdinalIgnoreCase, alma == Alma

CurrentCulture, őlma < Őlma
CurrentCultureIgnoreCase, őlma == Őlma
InvariantCulture, őlma < Őlma
InvariantCultureIgnoreCase, őlma == Őlma
Ordinal, őlma > Őlma
OrdinalIgnoreCase, őlma == Őlma

Nem teljes a teszt, de látható, hogy az ordinal másként sorrendez, mint az invariant, nem nyelvi szempontok szerint, ezért is nem való GUI-hoz, de tudja, hogy a kis ő-nek a nagy Ő a párja. Az invariant se elég okos persze, mivel általános, nem ismeri egy adott nyelv szabályait, pl.

Thread.CurrentThread.CurrentCulture = new CultureInfo(“hu-hu”);
a = “cukor”;
b = “csoki”;
Compare(a, b);

CurrentCulture, cukor < csoki CurrentCultureIgnoreCase, cukor < csoki InvariantCulture, cukor > csoki
InvariantCultureIgnoreCase, cukor > csoki
Ordinal, cukor > csoki
OrdinalIgnoreCase, cukor > csoki

A cukor után van a csoki magyarban, mivel van csé betűnk. Az invariant értelemszerűen nem tud erről, ezért a csokit cé, es, okinak olvassa, és így is rendez.

Ezek után egyet kell értsek a cikkel, használjuk kulturált kultúrát nyelvi rendezéshez, fájl elérési utakhoz, ojjektum nevekhez, stb. oridinalt, az invariantot meg felejtsük el.
Legalábbis ma így látom. Comments welcome.

Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

LEAVE A COMMENT

2 COMMENTS

  • NJoco September 23, 2009

    Mivel itt lakunk ahol, a kulturált rendezés az oks. Ha nem azt használod előbb vagy utóbb valami bugot okoz valahol, nekem az a tapasztalatom.

  • Soczó Zsolt September 23, 2009

    Egyetértünk, csak secu hibákat okozhat, ha mindent így csinálunk, lsd. a cikkben a török i, I problémáját. Ezért (is) kell tudni az invariantról és az ordinalról.