2009-04-27 13 views
13

Ok. Así que tengo un código que asigna ciertos controles en un WinForm a ciertas propiedades en un objeto, para hacer ciertas cosas a los controles cuando ciertas cosas suceden a los datos. Todo bien y bien, funciona bien. No es el problema. La cuestión es, para añadir elementos a la cartografía, que llama a una función que se parece a:Extracción de nombres de propiedades para reflexión, con Intellisense y compilación en tiempo de compilación

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

El problema me encuentro es que es muy difícil decir, en tiempo de compilación, la diferencia entre la línea anterior y el siguiente:

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

Desde el último parámetro es una cadena, no hay tiempo de compilación o algo por el estilo que se enfrente a los que la propia cadena en realidad corresponde a un nombre de propiedad válido en el objeto dado. Además, cosas como Refactor y "Buscar todas las referencias" se pierden en este tipo de referencia, lo que resulta en hilaridad cuando cambia el nombre de la propiedad. Entonces, lo que me pregunto es si hay alguna forma de cambiar la función, de modo que lo que estoy pasando siga siendo una cadena que represente el nombre de la propiedad de alguna manera, pero con la verificación en tiempo de compilación del valor real que entra. Alguien dijo que podría hacer esto con Expression Trees, pero los he leído y no veo la conexión. Me encantaría hacer algo como:

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

o incluso

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

sería dulce!

¿Alguna idea?

+0

Tardó 6 o 7 pasadas antes de que pudiera detectar la diferencia en sus dos líneas de código. – jjnguy

+0

Bienvenido a mi infierno ... ahora imagínenlo plagado de acrónimos como CPCR, CPR, CLI, etc ... – GWLlosa

+0

Durante mucho tiempo he deseado algún tipo de complemento VS que analice todas las cadenas de su el código y el hechizo los revisan. También tomaría en cuenta la envoltura de camello, y corregirá la ortografía de cada palabra individualmente. Alguien tiene que escribir ese tonto ... – BFree

Respuesta

14

en 3.5, Expresión es una manera de especificar nombres de miembros como código; podría tener:

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

y luego analizar el árbol de expresiones para obtener el valor. Un poco ineficiente, pero no está mal.

Aquí es código de ejemplo:

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" 
    } 

llamada con:

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

No debería estar pasando literalmente cadenas como nombres de propiedades. En su lugar, debe usar YourClass.PROPERTY_NAME_FOO.

Debe declarar estos Strings como constetos en su clase.

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"; 

O, usted no tiene que preocuparse de Cuerdas en absoluto, sólo los nombres de propiedades:

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

que pararía cadenas que no se refieren a una propiedad válida de entrar en las llamadas a funciones .

Intelisense podrá mostrarle los nombres de las propiedades de su clase como sugerencias para completar automáticamente.

+0

Pensé en eso, el Lo único que me molesta es que es redundante ... Ahora está manteniendo la información de la propiedad en 2 lugares separados (la cadena const y el nombre de la propiedad en sí) que parece ser menos que óptima. – GWLlosa

+0

Realmente no tiene que usar una Cadena. Podrías simplemente mapearlos a enteros o lo que sea. Es el nombre que es importante, y el hecho de que los valores son únicos. – jjnguy

+0

Pero si no los mapeo en cadenas, ¿cómo obtengo un valor de cadena? – GWLlosa

2

considerar el uso de lambdas o incluso System.Linq.Expressions para esto, con uno de:

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); 

A continuación, llame con

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

Utilice el argumento Expresión si necesita separar el árbol de sintaxis abstracta en tiempo de ejecución, para hacer reflexiones como obtener el nombre de la propiedad como una cadena; alternativamente, deje que el delegado haga el trabajo de buscar los datos que necesita.

3

Para hacer las cosas más fácil con la solución basada en la expresión lambda, que escribió como un método de extensión.

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; 
    } 

llamada como ésta

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); 

    } 

bien, usando como método de extensión es realmente por conveniencia como de hecho puede llamar al método en una clase de propiedades de las anteras clase. Estoy seguro de que podría mejorarse.

+0

Puedo ver algunos comentarios relacionados con el uso del método de extensión, la razón por la que quería tener esto en mis manos era para WPF Binding (mvvm) para que pueda llamar a OnPropertyChanged (this.GetProperty ((= = MyProperty); ayudando a aliviar algunos de los problemas en torno a la seguridad del tiempo de compilación de dicha vinculación wpf. Al menos si cambias el nombre de la propiedad obtienes algo de seguridad de tiempo de compilación. Sin embargo, tienes que editar el texto vinculante en tu xaml ... –

Cuestiones relacionadas