2009-04-27 5 views
13

Ok. Quindi ho un codice che associa certi controlli su un winForm a certe proprietà in un oggetto, per fare certe cose ai controlli quando accadono certe cose ai dati. Tutto bene, funziona bene. Non il problema Il problema è, per aggiungere elementi alla mappatura, che io chiamo una funzione che assomiglia a:Estrazione dei nomi di proprietà per il reflection, con Intellisense e Compile-Time Checking

this.AddMapping(this.myControl,myObject,"myObjectPropertyName"); 

Il problema che ho incontrato è che è molto difficile dire, al momento della compilazione, la differenza tra la linea di cui sopra e qui di seguito:

this.AddMapping(this.myControl,myObject,"myObjectPropretyName"); 

Dal momento che l'ultimo parametro è una stringa, non c'è tempo di compilazione il controllo o qualcosa di simile che potrebbe valere che la stringa stessa in realtà corrisponde a un nome di proprietà valida per l'oggetto dato. Inoltre, cose come Refactor e "Trova tutti i riferimenti" perdono questo tipo di riferimento, con conseguente ilarità quando cambia il nome della proprietà stessa. Quindi quello che mi chiedo è se c'è un modo per cambiare la funzione in modo tale che quello che sto passando è ancora una stringa che rappresenta il nome della proprietà in qualche modo, ma con il controllo del tempo di compilazione del valore attuale. Qualcuno ha detto che potrebbe farlo con Expression Trees, ma ho letto su di loro e non sembrano vedere la connessione. Mi piacerebbe fare qualcosa di simile:

this.AddMapping(this.myControl,myObject,myObject.myObjectPropertyName); 

o anche

this.AddMapping(this.myControl,myObject.myObjectPropertyName); 

sarebbe dolce!

Qualche idea?

+0

Sono occorsi 6 o 7 passaggi prima di poter individuare la differenza tra le due righe di codice. – jjnguy

+0

Benvenuti nel mio inferno ... ora immagino che sia pieno di acronimi come CPCR, CPR, CLI, ecc ... – GWLlosa

+0

Per il più lungo tempo ho desiderato un qualche tipo di add-in di VS che avrebbe analizzato tutte le stringhe nel vostro codice e controllo ortografico. Prenderà anche in considerazione il rivestimento del cammello e il controllo ortografico di ogni parola singolarmente. Qualcuno deve scrivere quel pollone .... – BFree

risposta

14

in 3.5, espressione è un modo per specificare i nomi dei membri come codice; è possibile:

public void AddMapping<TObj,TValue>(Control myControl, TObj myObject, 
     Expression<Func<TObj, TValue>> mapping) {...} 

e quindi analizzare l'albero delle espressioni per ottenere il valore. Un po 'inefficiente, ma non troppo male.

Ecco codice di esempio:

public void AddMapping<TSource, TValue>(
     Control control, 
     TSource source, 
     Expression<Func<TSource, TValue>> mapping) 
    { 
     if (mapping.Body.NodeType != ExpressionType.MemberAccess) 
     { 
      throw new InvalidOperationException(); 
     } 
     MemberExpression me = (MemberExpression)mapping.Body; 
     if (me.Expression != mapping.Parameters[0]) 
     { 
      throw new InvalidOperationException(); 
     } 
     string name = me.Member.Name; 
     // TODO: do something with "control", "source" and "name", 
     // maybe also using "me.Member" 
    } 

chiamato con:

AddMapping(myControl, foo, f => f.Bar); 
1

In realtà non si devono passare letterali String come nomi di proprietà. Invece dovresti usare YourClass.PROPERTY_NAME_FOO.

È necessario dichiarare queste stringhe come unità di comando nella propria classe.

public const String PROPERTY_NAME_FOO = "any string, it really doesn't matter"; 
public const String PROPERTY_NAME_BAR = "some other name, it really doesn't matter"; 

Oppure, non dovete preoccuparvi di stringhe a tutti, i nomi delle proprietà solo:

public const int PROPERTY_NAME_FOO = 0; 
public const int PROPERTY_NAME_BAR = 1; //values don't matter, as long as they are unique 

che arresterebbe stringhe che non si riferiscono a una proprietà valida di entrare in chiamate di funzione .

Intelisense sarà in grado di mostrare i nomi delle proprietà all'interno della classe come suggerimenti per il completamento automatico.

+0

Ho pensato a questo, il l'unica cosa che mi dà fastidio è che è ridondante ... Adesso stai mantenendo le informazioni sulle proprietà in 2 luoghi separati (la stringa const e il nome della proprietà stessa) che sembra non ottimale. – GWLlosa

+0

Davvero non devi usare una stringa. Potresti semplicemente mapparli a ints o altro. È il nome che è importante e il fatto che i valori siano unici. – jjnguy

+0

Ma se non li associo alle stringhe, come faccio a ottenere un valore di stringa? – GWLlosa

2

considerare l'utilizzo di lambda o anche System.Linq.Expressions per questo, con uno dei:

extern void AddMapping<T,U>(Control control, T target, Func<T,U> mapping); 
extern void AddMapping<T,U>(Control control, T target, Expression<Func<T,U>> mapping); 

Quindi chiamare con

this.AddMapping(this.myControl, myObject, (x => x.PropertyName)); 

Utilizzare l'argomento Espressione se è necessario separare l'albero di sintassi astratto in fase di runtime, per eseguire operazioni di riflessione come ottenere il nome della proprietà come stringa; in alternativa, lascia che il delegato svolga il compito di pescare i dati che ti servono.

3

Per rendere le cose più facili con la Lamda soluzione Expression base, ho scritto come un metodo di estensione.

public static string GetPropertyName<T>(this object o, Expression<Func<T>> property) 
    { 
     var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo; 
     if (propertyInfo == null) 
      throw new ArgumentException("The lambda expression 'property' should point to a valid Property"); 
     var propertyName = propertyInfo.Name; 
     return propertyName; 
    } 

chiamata come questo

class testclass 
    { 
     public string s { get; set; } 
     public string s2 { get; set; } 
     public int i { get; set; } 

    } 

    [TestMethod] 
    public void TestMethod2() 
    { 
     testclass x = new testclass(); 
     string nameOfPropertyS = this.GetPropertyName(() => x.s); 
     Assert.AreEqual("s", nameOfPropertyS); 

     string nameOfPropertyI = x.GetPropertyName(() => x.i); 
     Assert.AreEqual("i", nameOfPropertyI); 

    } 

Va bene, utilizzando come metodo di estensione è davvero per comodità, come si può infatti chiamare il metodo su una classe per le proprietà di corrispondenza con un altro di classe. Sono sicuro che potrebbe essere migliorato.

+0

Sono in grado di vedere alcuni commenti relativi all'uso del metodo di estensione, la ragione per cui volevo mettere le mani su questo era per WPF Binding (mvvm) in modo da poter chiamare OnPropertyChanged (this.GetProperty (() => MyProperty); aiutando ad alleviare alcuni dei problemi relativi alla sicurezza in fase di compilazione di tale associazione wpf. Almeno se si modifica il nome della proprietà si ottiene un po 'di sicurezza in fase di compilazione, ma è comunque necessario modificare il testo del binding in xaml ... –

Problemi correlati