Stringa, tu mi hai provocato… E io te distruggo

De gustibus tutta la vita, però io le stringhe cablate nel codice non le sopporto proprio. Una vita intera a tentare di evitarla e poi ti arriva MVVM che, a botte di implementazioni di INotifyPropertyChanged ti fa spuntare come funghi “robe” di questo tipo:

    private string emailAddress;
    /// <summary>
    /// Customer  e-mail address
    /// </summary>
    public string EmailAddress
    {
        get
        {
            return this.emailAddress;
        }
        set
        {
            this.emailAddress = value;
            RaisePropertyChanged("EmailAddress");
        }
    }

Terribile, nevvero? “Ispirandomi” ad un tip che scrissi per UGIdotNET un paio di anni addietro, mi son detto: perché non usare la stessa tecnica “lambda based” per risolvere il problema una volta per tutte? E quindi:

    public static class ReflectionHelper<T>
    {
        public static string GetPropertyName(Expression<Func<T, object>> subSelector)
        {
            if (subSelector.Body is MemberExpression)
            {
                return ((subSelector.Body as MemberExpression).Member as PropertyInfo).Name;
            }
            else
            {
                var memberPropertyAccessor = ((subSelector.Body as UnaryExpression).Operand.GetType().GetProperty("Member"));
                var memberPropertyValue = memberPropertyAccessor.GetValue((subSelector.Body as UnaryExpression).Operand, null);
                var namePropertyAccessor = memberPropertyValue.GetType().GetProperty("Name");
                var propertyName = (string)namePropertyAccessor.GetValue(memberPropertyValue, null);
                return propertyName;
            }
        }
    }

Che significa poter scrivere il primo snippet così:

   private string emailAddress;
   /// <summary>
   /// Customer  e-mail address
   /// </summary>
   public string EmailAddress
   {
       get
       {
           return this.emailAddress;
       }
       set
       {
           this.emailAddress = value;
           RaisePropertyChanged(
                   ReflectionHelper<CustomerDetailViewModel>.GetPropertyName(vm => vm.EmailAddress)
               );
       }
   }

Non so voi, ma IMVHO il solo fatto di poter fare refactoring in modo “sicuro” ha giustificato i 10 minuti spesi a scrivere la funziona helper. E’ una lambda: leggere attentamente le avvertenze, usare con cautela.

P.S.: giusto il tempo di portare l’implementazione dallo stato di “funziona” a quello di “fa meno schifo a leggerla” nonché di vedere che i test continuano a dare luce verde, e la infilo in NSK ad imperitura memoria

P.P.S.: Buona anche l’idea di “infilare” la funzione helper in un eventuale classe ViewModelBase, così da evitare la prima parte della invocazione e rendere il codice meno prolisso

Technorati Tag: ,,,

posted @ mercoledì 9 giugno 2010 15:24

Print

Comments on this entry:

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by M.rkino at 09/06/2010 15:41
Gravatar
Mi piace, concordo che anche io sto guardando con un leggero disappunto l'uso di stringhe nel codice quando devo implementare "INotifyPropertyChanged". Sarebbe fantastico se giocando con classi base e/o metodi estesi si arrivasse a scrivere qlcosa del tipo

RaisePropertyChanged(this.GetPropertyName(vm => vm.EmailAddress));

o ancora meglio:

RaisePropertyChanged(vm => vm.EmailAddress);

Cmq, ottimo post... e viva le lambda!

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by Stefano Ottaviani at 09/06/2010 16:03
Gravatar
Aggiungo che oltre al MVVM, un altro tipico campo di applicazione è dato dalle Criteria query di NHibernate (o anche le HQL), (in attesa della versione 3)

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by fremsoft at 09/06/2010 18:30
Gravatar
Odio il codice prolisso, ma devo farti i complimenti perchè è un'ottima implementazione delle reflection!

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by Ricciolo at 09/06/2010 22:58
Gravatar
Ma così hai usato due stringhe nell'implementazione di GetPropertyName :-P

Molto carino, però mi domando se non sia troppo. Qualche istruzione in più solo per avere qualcosa di più elegante e nessun altro beneficio.
In fondo il MVVM lo fai perche vuoi testare (solitamente) e se testi dovresti individuare le proprietà rinominate.
Considerando poi che il compilatore sotto crea l'espressione e aggiunge altre istruzioni allora tanto vale usare qualcosa come PostSharp e far scrivere direttamente da lui OnPropertyChanged("membro").

Non ho comunque trovato una soluzione definitiva che mi piaccia, quindi proverò anche questa :-)

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by M.rkino at 10/06/2010 01:17
Gravatar
@Ricciolo il beneficio non sta nell'eleganza, ma nel fatto che sarà più difficile far scappare errori in quanto il compilatore ci potrà aiutare ;-)

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by Alessandro Scardova at 10/06/2010 01:56
Gravatar
Il problema è che il binding nello xaml rimane scritto in modo stringoso... conseguenza: in caso di refactoring non abbiamo un "banale" errore di runtime ma un subdolo errore di binding... Panico Paura :D

# re: Stringa, tu mi hai provocato… E io te distruggo

Left by Ricciolo at 10/06/2010 16:44
Gravatar
@Andrea Saltarello: scusami, mi son spiegato male. L'obiettivo, facilitare il refactoring, lo condivido e anche a me non piace mettere le stringhe, ma la scelta a questo approccio è spinta dall'eleganza.

Quello che il compilatore genera è del codice neanche piuttosto banale che fa uso di reflection. Prima di linq potevi fare la stessa cosa usando lo StackTrace senza neanche indicare il membro, lo fa da solo, e allora come adesso dovrebbe far storcere il naso. E' secondo me un uso improprio di LINQ e una soluzione a runtime per risolvere un problema di compiletime.

Your comment:



 (will not be displayed)


 
 
Please add 2 and 3 and type the answer here:
 

Live Comment Preview:

 
«aprile»
domlunmarmergiovensab
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011