Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Invocare un Metodo con una Culture Personalizzata

Posted by Ricibald on February 7th, 2008

Per formattare le date o i numeri in un certo modo si possono utilizzare le tecniche di formattazione.

Quindi, se ad esempio vogliamo esprimere la data in un formato particolare, possiamo utilizzare:

DateTime date = DateTime.Now;
string format = date.ToString("MM/dd/yyyy g");

Per maggiore flessibilità possiamo immaginare di inserire il formato in una chiave di configurazione e il gioco è fatto.

Ma ora immaginiamo che ci arrivi una nuova richiesta dal nostro fido cliente:

Voglio i numeri a virgola mobile formattate come “xx.yy”.

Ora, dovremmo quindi andare a modificare tutti i double come:

double d = 2.2;
string format = d.ToString("00.00");

Il problema è quindi che le modifiche ai formati non sono sotto controllo. Esistono due modi per mantenerli sotto controllo, utilizzando le CultureInfo:

  • invocare dove possibile ToString(IFormatProvider), passando una CultureInfo creata al momento dell’inizializzazione
    • ma richiede un’accortezza di chi scrive codice
    • …e una bella scocciatura…
  • specificare la CultureInfo dell’assembly nel file di configurazione
    • deve essere o standard o creata appositamente e registrata
      • chi la crea? chi la registra?
    • vincolo tutto il progetto ad utilizzare quella CultureInfo: come ripristinare il comportamento originario?

Dall’altra parte è vero che la CultureInfo del thread corrente potrebbe essere impostata come si vuole, ma bisogna anche fare i conti con il multi-threading, specialmente vero in applicazioni a interfaccia grafica o web.

Bisognerebbe quindi:

creare un thread con l’unico scopo di isolare il cambiamento di CultureInfo

Si vuole quindi rendere disponibile una funzione che in modo generico faccia questo:

class BilancioCreator {
    public Bilancio CreaBilancio(int annoBilancio)
    {
        return CustomCultureInfoInvoker.InvokeMethod<Bilancio>(new CustomCultureInfo(), delegate()
        {
            return CreaBilancioImpl(annoBilancio);
        });
    }

    private Bilancio CreaBilancioImpl(int annoBilancio)
    {
        string data = DateTime.Now;
        Bilancio result = new Bilancio(data.ToString());
        result.CalcolaBilancio(annoBilancio);
        return result;
    }
}

class CustomCultureInfo : CultureInfo
{
    public CustomCultureInfo()
        : base("en-US")
    {
        this.NumberFormat.CurrencyDecimalSeparator = ".";
        this.NumberFormat.CurrencyDecimalDigits = 0;
        this.NumberFormat.CurrencyGroupSeparator = "";
        this.NumberFormat.CurrencyNegativePattern = 5;
        this.NumberFormat.CurrencySymbol = "";
        this.NumberFormat.NumberDecimalDigits = 0;

        /* Il DateTime.ToString combina ShortDatePattern con LongTimePattern */
        this.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
        this.DateTimeFormat.LongTimePattern = "";
    }
}

Si mette quindi a disposizione il codice che consente questo, per chiunque ne fosse interessato.

public static class CustomCultureInfoInvoker
{
    public delegate T Method<T>();
    public static T InvokeMethod<T>(CultureInfo culture, Method<T> method)
    {
        Exception exceptionThread = null;
        T result = default(T);
        Thread customCultureThread = new Thread(delegate()
        {
            try
            {
                result = method.Invoke();
            }
            catch (Exception ex)
            {
                exceptionThread = ex;
            }
        });
        customCultureThread.CurrentCulture = culture;
        customCultureThread.CurrentUICulture = culture;
        customCultureThread.Start();
        customCultureThread.Join();

        /* Rilancia eventuali errori */
        if (exceptionThread != null)
        {
            throw new Exception("Errore: " + exceptionThread.Message, exceptionThread);
        }
        return result;
    }
}

Notare che per i valori “decimal” il discorso è un po’ diverso, poiché in questo caso la CultureInfo non consente di fare proprio tutto. Per questo si veda il precedente post per cambiare blocchi di valori dello stesso tipo.

Leave a Reply

You must be logged in to post a comment.