2009-09-23 21 views
6

Esto probablemente se aplica a otros lugares, pero en WinForms, cuando utilizo el enlace me parece que muchos métodos quieren tomar el nombre de la propiedad para enlazar. Algo así como:¿Hay alguna manera de hacer que la unión de C# funcione de manera estática?

class Person 
{ 
    public String Name { get { ... } set { ... } } 
    public int Age { get { ... } set { ... } } 
} 

class PersonView 
{ 
    void Bind(Person p) 
    { 
     nameControl.Bind(p,"Name"); 
     ageControl.Bind(p,"Age"); 
    } 
} 

El gran problema Sigo teniendo con esto es que "Nombre" y "edad" son representados como cadenas. Esto significa que el compilador no es de ayuda si alguien renombra una de las propiedades de la persona. El código compilará bien, pero las consolidaciones se romperán.

¿Hay alguna manera estándar de solucionar esto que me he perdido? Parece que necesito una palabra clave, tal vez llamada stringof para que coincida con el typeof existente. Se podría utilizar algo como:

ageControl.Bind(p,stringof(p.Age).Name); 

stringof podría volver alguna clase que tiene propiedades para obtener la ruta completa, parte de la ruta, o la cadena para que pueda analizarlo usted mismo.

¿Algo como esto ya se puede hacer?

+1

Esto no es C# que se une de forma dinámica que gran parte, esto son WinForms. –

+0

Es cierto. Este tipo de función de idioma puede ser útil, incluso si no está utilizando WinForms. –

Respuesta

2

Se pueden utilizar expresiones para obtener compilaciones verificadas por el compilador. Por ejemplo, en uno de los proyectos actuales establecimos fijaciones de este tipo:

DataBinder 
    .BindToObject(this) 
    .ObjectProperty(c => c.IsReadOnly) 
     .Control(nameTextBox, n => n.ReadOnly) 
     .Control(addressControl, n => n.ReadOnly) 

Código apoyar este estilo se separa en varias clases:

public static class DataBinder 
{ 
    public static DataBinderBindingSourceContext<TDataSource> BindToObject<TDataSource>(TDataSource dataSource) 
    { 
     return new DataBinderBindingSourceContext<TDataSource>(dataSource); 
    } 
} 

public class DataBinderBindingSourceContext<TDataSource> 
{ 
    public readonly object DataSource; 

    public DataBinderBindingSourceContext(object dataSource) 
    { 
     DataSource = dataSource; 
    } 

    public DataBinderControlContext<TDataSource, TProperty> ObjectProperty<TProperty>(Expression<Func<TDataSource, TProperty>> property) 
    { 
     return new DataBinderControlContext<TDataSource, TProperty>(this, property); 
    } 
} 

public class DataBinderControlContext<TDataSource, TProperty> 
{ 
    readonly DataBinderBindingSourceContext<TDataSource> BindingSourceContext; 
    readonly string ObjectProperty; 

    public DataBinderControlContext 
     (
      DataBinderBindingSourceContext<TDataSource> bindingSourceContext, 
      Expression<Func<TDataSource, TProperty>> objectProperty 
     ) 
    { 
     BindingSourceContext = RequireArg.NotNull(bindingSourceContext); 
     ObjectProperty = ExpressionHelper.GetPropertyName(objectProperty); 
    } 

    public DataBinderControlContext<TDataSource, TProperty> Control<TControl>(TControl control, Expression<Func<TControl, TProperty>> property) 
     where TControl : Control 
    { 
     var controlPropertyName = ExpressionHelper.GetPropertyName(property); 
     control.DataBindings.Add(controlPropertyName, BindingSourceContext.DataSource, ObjectProperty, true); 

     return this; 
    } 
} 

public static class ExpressionHelper 
{ 
    public static string GetPropertyName<TResult>(Expression<Func<TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Skip(1).Join("."); 
    } 

    public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Join("."); 
    } 

    static IEnumerable<string> GetMemberNames(Expression expression) 
    { 
     if (expression is ConstantExpression || expression is ParameterExpression) 
      yield break; 

     var memberExpression = (MemberExpression)expression; 

     foreach (var memberName in GetMemberNames(memberExpression.Expression)) 
      yield return memberName; 

     yield return memberExpression.Member.Name; 
    } 
} 

public static class StringExtentions 
{ 
    public static string Join(this IEnumerable<string> values, string separator) 
    { 
     if (values == null) 
      return null; 

     return string.Join(separator, values.ToArray()); 
    } 
} 
0

Se podría utilizar la reflexión para encontrar el nombre ;-)

Por supuesto, esto sería una referencia circular, tendrá que utilizar el nombre que usted piensa que es encontrar el mismo nombre (o al no encontrar nada , lo que significa que se cambió el nombre de la propiedad ... Pero hay una idea (o más bien, un truco): haciendo una referencia de "no hacer nada" a la propiedad que desea utilizar, obtendría la confirmación de tiempo de compilación de que todavía está allí. El problema es si alguien simplemente intercambia varios nombres de propiedad, en ese caso, los nombres aún existen (sin error en tiempo de compilación), pero tienen una semántica diferente de aplicación (posibles sorpresas en la salida de la aplicación)

4

Eche un vistazo a esto code snippet He publicado otra pregunta, ¡puede ayudarte! (Pero sólo, si está utilizando .NET 3.5)

Saludos
Oliver Hanappi

4

Usted puede hacer eso con los árboles de expresión, como se explica in this question

protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression) 
{ 
    if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess) 
    { 
     PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo; 
     if (prop != null) 
     { 
      return prop.Name; 
     } 
    } 
    throw new ArgumentException("expression", "Not a property expression"); 
} 

... 

ageControl.Bind(p, GetPropertyName((Person p) => p.Age)); 
Cuestiones relacionadas