Programming Languages Hacks

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

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Sincronizzare Oggetti e Controlli Web/Windows In Un Colpo Solo

Posted by Ricibald on August 14th, 2008

L’utilizzo dei controlli Web/Windows prevede due fasi:

  • il trasferimento dei dati dall’oggetto ai controlli corrispondenti
  • il trasferimento dei dati dai controlli all’oggetto corrispondente

Come dire:

void loadPage(TextBox txt_nome, Persona persona) {
    txt_nome.Text = persona.Nome;
}

void submitInfo(TextBox txt_nome, Persona persona) {
    persona.Nome = txt_nome.Text;
}

Questo è il classico approccio, ma, come tutte le cose meccaniche che si fanno in informatica, nasconde vari problemi:

  1. il tempo: scrivo le stesse cose due volte. Prima ad andare e poi a tornare…
  2. la consistenza: qualche proprietà potrei dimenticarla e l’oggetto potrebbe non essere aggiornato o potrebbe essere popolato con il valore sbagliato

La cosa ideale sarebbe avere un controllo Web/Windows che consentisse di realizzare il bind a due vie (le due fasi le fa il controllo per noi):

void bind(TextBoxBinded txt_nome, Persona persona) {
    txt_nome.Bind(persona.Nome);
}

Questa “sarebbe” la cosa ideale, ma non è praticabile: come conosciamo il set della proprietà “Nome”? In questo modo non possiamo:

Come passare per parametro i metodi get/set di una proprietà o di un campo, in modo da ottenere il bind a due vie?

Dopo ricerche su internet, miei ragionamenti e varie ed eventuali, le soluzioni possibili sono le seguenti:

  1. Approccio basato su Reflection: la soluzione più immediata, ma molto fragile: per ottenere un oggetto Property dobbiamo passare il nome della proprietà. Tale nome è una semplice stringa: non è pertanto tipizzato in alcun modo ed eventuali modifiche alla proprietà non ci verrebbero segnalate dal compilatore… Con questo approccio ogni modifica alla struttura ad oggetti diventerebbe letteralmente un incubo…
  2. Ogni proprietà implementa un interfaccia specifica: ad esempio IProperty<T>, dove T è il tipo della proprietà. Tale interfaccia avrà metodi get e set che il controllo Web/Windows riconosce. Il metodo bind accetterà quindi solo oggetti di tipo IProperty. L’approccio ha un ulteriore vantaggio: l’interfaccia può essere pensata come wrapper per contenere metainformazioni. Non solo quindi get o set, ma anche informazioni come editabilità, visibilità, nome, …
    Per abilitarla si devono modificare tutte le proprietà in modo che il tipo non sia ad es. String, ma IProperty<String>: questo però provoca una modifica nell’interfaccia della proprietà, e tutti i metodi che prima utilizzavano ad es. persona.Nome.ToUpper non funzioneranno più.
    Una soluzione eccellente stà nel realizzare degli operatori di conversione impliciti da IProperty<String> a String e nell’implementare le stesse identiche funzioni di String in un suo proxy: PropertyString, che implementa IProperty<String> e dichiara le stesse funzioni di String, rigirando tali funzioni al valore String che contiene.
    Con questo approccio si possono trattare a tutti gli effetti gli IProperty<T> come se fossero effettivamente dei T: i client ne possono essere inconsapevoli…
    Si hanno però svantaggi seguenti:

    • si deve aver accesso alla modifica delle proprietà delle classi. Se della struttura ad oggetti non abbiamo il controllo l’unica soluzione che ci rimane per continuare ad usare il nostro approccio è realizzare un Adapter. Una soluzione piuttosto noiosa…
    • si devono implementare tutte le classi che emulano i tipi primitivi: PropertyString, PropertyInt,  PropertyBool, … è anche vero che una volta fatto diventa patrimonio vostro e rimarrà lì con voi…
  3. Approccio basato su Reflection Tipizzata Fortemente: una splendida soluzione basata su LambdaExpression e Generics proposta da Nick Butler. Il segreto è utilizzare i Generics per dichiarare tramite LambdaExpression la proprietà da “bindare”, più o meno in modo simile alla OrderBy di LINQ:
    PropertyInfo prop = typeof(Persona).GetProperty("Nome"); // VECCHIO APPROCCIO: NON TIPIZZATO
    PropertyInfo prop = Instance<Persona>.GetProperty( o => o.Nome);

    Dove avremo:

    public static class Instance<TClass>
    {
        public static PropertyInfo GetProperty<T>(
                              Expression<Func<TClass, T>> m ) 
        {
            LambdaExpression lambda = m;
            Expression bodyExpr = lambda.Body;
            MemberExpression memberExpr = (MemberExpression)bodyExpr;
            MemberInfo memberInfo = memberExpr.Member;
            PropertyInfo propInfo = (PropertyInfo)memberInfo;
            return propInfo;
        }
    }

    Nonostante l’eleganza dell’approccio (specie se combinato con extension method (si veda l’implementazione di TextBoxFor nell’articolo di MSDN), questo ha un punto in svantaggio rispetto al precedente: consente di inserire solo metadati statici tramite attributi. Viceversa, il vantaggio rispetto alla precedente è che non è necessario modificare nulla del codice preesistente e non bisogna implementare i proxy per ogni tipo primitivo

Insomma, ora che sapete tutto manca “solo” di scrivere le vostre versioni custom dei controlli che consentono il bind (consiglio un Decorator…)

Leave a Reply

You must be logged in to post a comment.