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 14th August 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…)

Posted in .net | 1 Comment »

Analisi sull’Uso dei Generics

Posted by Ricibald on 19th May 2008

Mi va di trattare un argomento “banale”, su cui vi concedo 5 minuti di risposte possibili:

Quali sono gli usi dei generics?

Pensateci, pensateci e pensateci… Dopo che avete pensato a tutte le possibili risposte andate avanti a leggere…

Gli utilizzi dei generics possono essere i seguenti:

  • contenitori: i generics consentono di definire contenitori tipizzati. Possiamo quindi definire una collezione di oggetti Persona, in modo tale che non risulti possibile aggiungere oggetti che non siano di tipo Persona
  • funtori tipizzati: è possibile specificare un delegate generico, da utilizzare in funzioni di decorazione
  • decoratori: consente di aggiungere le funzionalità a un tipo. Immaginiamo di avere una classe Decorator<T>, che non fa altro che aggiungere una funzione Decorate() al tipo T, e consente di ottenere la proprietà avvolta di tipo T tramite il metodo GetValue(). Risulta molto utile questa funzionalità anche in contesti di metainformazioni: immaginiamo infatti di avere una classe Property<T> che rappresenta una proprietà: avremo metodi come GetValue(), SetValue(), IsReadOnly, IsRequired, IsEnabled, Description, … Attraverso questo utilizzo possiamo esprimere in modo fortemente tipizzato il concetto di proprietà (altrimenti saremmo stati costretti a richiedere tramite reflection le proprietà: un approccio fragile, poiché non solleva errori a tempo di compilazione in caso di proprietà inesistente)
  • proxy delle proprietà: come caso particolare dei decoratori, consente di controllare l’accesso a una certa proprietà secondo determinati vincoli di sicurezza. Consideriamo quindi una classe Proxy<T>: all’accesso di T verranno eseguiti controlli di sicurezza o inserimento in cache o altri tipi di controlli
  • vincoli di progettazione: si può pensare di introdurre delle classi a granularità architetturale, come ObjectModel<T>, DataObject<ObjectModel>, BusinessObject<DataObject>: consentono di definire il comportamento comune al bridge che lega le classi. Sappiamo infatti che il BusinessObject richiede al DataObject di creare un ObjectModel. Risulta utile creare delle classi che mettono in comune questo comportamento, in modo che sia scritto una volta per tutte. In generale, questo approccio consente di far rispettare dei vincoli di progettazione in un framework definito
  • scelta sottoclasse concreta a tempo di compilazione: design pattern come Strategy prevedono la definizione della strategia nell’ambito della classe Context, da valutare a runtime. La classe Context potrebbe essere generica: Context<IStrategy>, dove IStrategy rappresenta la strategia scelta. Questo approccio risulta molto utile per definire la classe concreta a tempo di compilazione e ottenere un guadagno prestazionale
  • specializzazione: in C# le proprietà statiche all’interno dei generics consentono di definire una primitiva forma di specializzazione. Queste verranno aggiornate solo con lo stesso tipo T utilizzato e consentono di tracciare informazioni statistiche sull’utilizzo di un certo T.

Posted in .net | No Comments »

Il Problema della Non-Covarianza nei Generics in Java e C#

Posted by Ricibald on 11th April 2008

Java, C# e C++ non contemplano il concetto di covarianza:

Una classe C<T> si dice covariante rispetto a T se, date due classi A e B tali che B deriva da A, risulta anche che C<B> deriva da C<A>.

Significa quindi che questa istruzione solleva un’eccezione:

List<Persona> studenti;
List<Object> objects = (List<Object>) studenti; // Errore

Questo deriva da un motivo di logica Object Oriented, non strutturale del linguaggio. Infatti il cast verso “objects” altera il comportamento di “studenti”, che accetta come elementi della lista solo oggetti che derivano da “Studente”. Se fosse stato ammesso, ciclare la collezione “studenti” avrebbe potuto restituire oggetti anche non di tipo “Studente” che non è ammesso. In altre parole, il problema è che la covarianza consente di definire sottoclassi che restringono la classe base.

Il concetto di covarianza si ripercuote sull’utilizzo dei metodi. Ad esempio: immaginiamo il metodo di una nostra implementazione della classe List:

class List<T> {
    AddAll(List<T> l);
}

Immaginiamo di avere una classe Studente che deriva da Persona:

List<Studente> studenti = new List<Studente>();
List<Persona> persone = new List<Persona>();
studenti.AddAll(studenti); // Ok
studenti.AddAll(persone);  // Errore

Il codice funziona se introduciamo wildcard (Java) o naked constraint (C#):

// Java
class List<T> {
    addAll(List<? extends T> l);
}
// C#
class List<T> {
    AddAll<U>(List<U> l) where U : T;
}

Se invece vogliamo staccarci dalla genericità e trattare solo istanze di tipo “Object” osserviamo (come abbiamo già visto) che questo non compila:

List<Object> l = (List<Object>)persone; // Errore

Ma utilizzando wildcard (Java) o collezioni raw (C#) la classe compila:

List<?> l = persone; // Java
IList l = persone;   // C#

Da cui deriva che in C#, quando realizziamo una classe generica, è necessario fornire sempre un’implementazione non generica per supportare upcast non generici:

// C#
class List {
    Add(object item);
    AddAll(List l);
}
class List<T> : List {
    new Add(T item);
    new AddAll<U>(List<U> l) where U : T;
}

Si noti infine il differente approccio Java/C#:

  • Le librerie Java forniscono un supporto al problema dell’upcast:
    • I metodi sono già predisposti (si veda addAll, che accetta in input una Collection<? extends E>)
    • Il cast verso tipi raw è di default supportato (List<?>)
  • Le librerie C# non sono pensate per il supporto verso l’upcast:
    • I metodi non prevedono naked constraint (AddAll accetta in input solo una List<T>). I naked constraint sembrano più che altro un workaround non dichiarato
    • Il cast verso tipi raw è fornito solo se viene fornita un’implementazione non generica della classe (è quindi richiesto l’intervento implementativo dello sviluppatore)

Posted in .net, java | 3 Comments »