Určení kultury pro interpolaci řetězců v C#

Visual Studio General .NET Core

4 years ago

Používání interpolace řetězců v C# je silně návykové. Musíme si však dát pozor, aby náš program opravdu fungoval i na počítačích uživatelů z jiných zemí. Představme si, že píšeme kód, který komunikuje s JavaScriptem – například ve Web Assembly – a potřebuje zavolat funkci která přijímá na vstupu jako parametr desetinné číslo:

Přestože kód vypadá na první pohled jednoduše, ve skutečnosti se v něm skrývá zásadní problém. Jaký? Funkce interpolace řetězců byla přidána do jazyka jako alternativa pro metodu string.Format. Dokonce si můžeme ve vygenerovaném IL kódu všimnout, že se v tomto případě metoda string.Format přímo volá:

Výstup string.Format je závislý na aktuální hodnotě CultureInfo.CurrentCulture a tedy stejně tak tomu je i v případě interpolace řetězců. Když tedy výše uvedený kód spustíme na počítači se systémem nastevným na en-US (americká angličtina), dostaneme následující výstup:

Naopak v případě počítače s nastavením cs-CZ (český jazyk) dostaneme:

Pokud bychom číslo zobrazovali uživateli, byla by desetinná čárka rozhodně vhodná. Zde by to však znamenalo volání funkce myFunction se dvěma argumenty – 1 a 2! Jako řešení tohoto problému bychom mohli použít metodu string.Format přímo a předat jí argument IFormatProvider pro určení konkrétního způsobu formátování. Můžeme však najít řešení, u kterého nepřijdeme o možnost použít interpolaci řetězců?

Řešení

Jak jsem zmínil, ve většině případů je kód s interpolovanými řetězci zkompilování na jednoduché volání string.Format . Pokud to však potřebujeme, může se stejná syntaxe vyhodnotit také jako instance System.FormattableString. Tato užitečná třída je ve skutečnosti obálkou pro interpolovaný řetězec a nabízí přístup k formátovacímu řetězci, k jeho argumentům, a také statickou metodu Invariant. Té můžeme předat jako argument instnaci FormattableString (náš interpolovaný řetězec), která je pak vyhodnocena vzhledem k invariantní kultuře (CultureInfo.InvariantCulture):

To je ideální, protože invariantní kultura zůstává vždy stejná a můžeme se spolehnout, že nezávisle na jazyce systému, bude výstupem vždy:

Tip: Psát opakovaně FormattableString.Invariant se může omrzet. Pokud metodu voláte často, můžete použít deklaraci using static, která byla představena v C# 6 a s ní se pak odkazovat an metodu přímo názvem Invariant:

Vlastní výběr kultury

Možná přemýšlíte, zda existuje metoda, kterou lze vyhodnotit interpolovaný řetězec pod libovolnou vybranou kulturou. Jednoduchý pokus nás může vést na následující kód:

To však nefunguje. Protože neprovádíme explicitně přetypování na FormattableString, kód je automaticky zkompilován jako jednoduché volání string.Format stejně jak jsme to viděli dříve. My pak už voláme ToString(usCulture) na výsledném řetězeci. A implementace string.ToString samy o sobě neobsahují žádnou logiku:

Namísto toho potřebujeme udělat malý cimrmanovský "krok stranou":

Explicitním uložením interpolovaného řetězce do proměnné FormattableString můžeme nyní volat přímo metodu FormattableString.ToString(IFormatProvider), která již opravdu vyhodnocuje s ohledem na kulturu předanou argumentem.

Zdrojový kód

Ukázkový zdrojový kód pro tento článek můžete najít na mém GitHubu. Ukazuje to všechny diskutované možnost v jednoduché konzolové .NET Core 3.0 aplikaci:

Invariant culture sample

Ukázková aplikace

Shrnutí

Interpolace řetězců v C# je velmi praktická může zestručnit náš kód. Musíme se však ujistit, že náš řetězec bude správně vyhodnocen vzhledem ke kontextu, ve kterém bude následně použit. Pokud jej zobrazíme uživateli, je výchozí chování využívající CultureInfo.CurrentCulture nejvhodnější. Na druhou stranu, při komunikaci typu stroj-stroj budeme spíše preferovat formátování pomocí FormattableString.Invariant.