Trochę więcej kultury

Rozszerzenie poprzedniego wpisu Trochę kultury!.

string.Format(…) pattern

Rozpoznaję taki jakby pattern przeładowanych metod dla string.Format(…). Jest wykorzystywany w wielu miejscach we Frameworku jak i poza nim.

string.Format pattern C# code

Istotne są pierwsze i ostatnie przeładowanie:

string.Format(IFormatProvider provider, string format, params object[]);
string.Format(string format, params object[] args);

Idea jest taka, że te pierwsze jest dobre, a te ostatnie złe. Używajmy pierwszego 🙂
Pozostałe przeładowania związane są z optymalizacją – osobny temat.

Chciałem zwrócić uwagę na ILog.DebugFormat() w bibliotece log4net oraz na Console.WriteLine() (ok, prawie – WriteLine nie posiada wersji z IFormatProvider – można z tym żyć w dzisiejszym przykładzie).

Przypadek loggera

        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        public static void LoggerFoo()
        {
            float floatingValue = 72.05f;
            
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            Log.DebugFormat("{0}", floatingValue); // 72.05

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("pl-pl");
            // "pl-pl" is not used, invariant culture formatting is used implicitly 
            Log.DebugFormat("{0}", floatingValue); // 72.05

            // explicit call formating with polish culture
            Log.DebugFormat(CultureInfo.CurrentCulture, "{0}", floatingValue); // 72,05
        }

Jak widzimy, w log4net można też zawołać metodę, która jest odpowiednikiem string.Format(). Jako pierwszy parametr można, ale nie trzeba przekazać kulturę.

Domyślnie wybierana jest CultureInfo.InvariantCulture. Jest to logiczne, ponieważ ktoś przeglądający logi (lub automat) chce widzieć jeden standardowy format daty czy liczby.

Przypadek Console.WriteLine()

    public static void ConsoleBar()
    {
        var now = DateTime.Now.Date;

        Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
        Console.WriteLine("{0}", now); // 10/29/2012 00:00:00

        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("pl-pl");
        Console.WriteLine("{0}", now); // 2012-10-29 00:00:00
    }

Domyślnie zostanie użyte Thread.CurrentThread.CurrentCulture (analogicznie do string.Format()). Jest to całkiem logiczne, ponieważ wynik będzie pokazany użytkownikowi.

Podsumowanie dwóch przypadków

Niby wszystko wygląda w porządku. Każda klasa zachowuje się inaczej, a mimo to działa dobrze. Jest jednak kilka „ale”, związanych z tym, że takich podobnych klas jest wiele i nie wszystkie są dobrze pomyślane.

Czasem ten domyślny może być w innym kontekście domyślny, a my się z kontekstem nie wstrzelimy. A czasem będą przypadki, że to co domyślne wcale nie będzie takie oczywiste i w 80% procentach dobre.

ReSharper też nie wie

A na koniec dowód, że nawet sam ReSharper się w tym wszystkim pogubił.

resharper redundant call to string.Format

        public static void ReSharperCase()
        {
            float floatingValue = 72.05f;

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("pl-pl");
            Log.DebugFormat("{0}", floatingValue); // 72.05
            
            Log.DebugFormat(string.Format("{0}", floatingValue)); // 72,05
        }

To tylko narzędzie, nie zwalnia z myślenia.

EDIT
Zapomniałem podkreślić, że jeżeli sam wystawiasz dla klientów twego kodu metodę Foo(string format, params object[] args) to zadbaj również o wersję z IFormatProvider.

Reklamy
Ten wpis został opublikowany w kategorii Uncategorized i oznaczony tagami , , , , , . Dodaj zakładkę do bezpośredniego odnośnika.

3 odpowiedzi na „Trochę więcej kultury

  1. Pingback: dotnetomaniak.pl

  2. Arek Bal pisze:

    Proponuję by IFormatProvidera ustawiać jako ostatni parametr i ustaiwać mu domyślego providera. Dev wtedy przy wywoływaniu będzie widział co mu tam twórca biblioteki ustawił. I jak po tym kroku zacznie sam psuć, to już jego wina. ^^

    • @Arek To Ci się nie skompiluje. Ostatnim parametrem jest lista objektów pobierana jako „params object[] args”, czyli o dowolnej długości. Nie możesz po tym dołożyć żadnego argumentu.

Możliwość komentowania jest wyłączona.