2009-10-18 28 views
7

Desde my recent question, trato de centralizar el modelo de dominio incluyendo alguna lógica absurda en la interfaz de dominio. Sin embargo, encontré algunos problemas que necesitan incluir o excluir algunas propiedades de la validación.¿Cuál es la mejor manera de definir y acceder a las propiedades seleccionadas en C#?

Básicamente, puedo usar el árbol de expresiones como el siguiente código. Sin embargo, no me gusta porque necesito definir la variable local ("u") cada vez que creo una expresión lambda. ¿Tienes algún código fuente que sea más corto que yo? Además, necesito algún método para acceder rápidamente a las propiedades seleccionadas.

public void IncludeProperties<T>(params Expression<Func<IUser,object>>[] selectedProperties) 
{ 
    // some logic to store parameter 
} 

IncludeProperties<IUser> 
(
    u => u.ID, 
    u => u.LogOnName, 
    u => u.HashedPassword 
); 

Gracias,

Respuesta

9

Lambdas son excelentes para muchos escenarios - pero si usted no quiere que ellos, tal vez simplemente no hace uso de ellos? Odio decirlo, pero las cadenas simples son probadas, especialmente para escenarios como el enlace de datos. Si desea un acceso rápido, puede ver HyperDescriptor, o hay formas de compilar un delegado en los descriptores de acceso de la propiedad, o puede compilar un Expression desde la cadena y compilarlo (incluyendo un molde a object si desea una firma conocida, en lugar de llamar al (mucho más lento) DynamicInvoke).

Por supuesto, en la mayoría de los casos, incluso el reflejo crudo es lo suficientemente rápido, y no es el cuello de botella.

Sugiero comenzar con el código más simple, y compruebe que es en realidad demasiado lento antes de preocuparse de que sea rápido. Si no es demasiado lento, no lo cambie. Cualquiera de las opciones anteriores funcionaría de otra manera.


Otra idea; si está utilizando Expression, usted podría hacer algo como:

public void IncludeProperties<T>(
    Expression<Func<T,object>> selectedProperties) 
{ 
    // some logic to store parameter 
} 

IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword }); 

y luego tomar la expresión separados? Un poco más ordenado, al menos ... aquí algunos ejemplos de código que muestra la deconstrucción:

public static void IncludeProperties<T>(
    Expression<Func<T, object>> selectedProperties) 
{ 
    NewExpression ne = selectedProperties.Body as NewExpression; 
    if (ne == null) throw new InvalidOperationException(
      "Object constructor expected"); 

    foreach (Expression arg in ne.Arguments) 
    { 
     MemberExpression me = arg as MemberExpression; 
     if (me == null || me.Expression != selectedProperties.Parameters[0]) 
      throw new InvalidOperationException(
       "Object constructor argument should be a direct member"); 
     Console.WriteLine("Accessing: " + me.Member.Name); 
    } 
} 
static void Main() 
{ 
    IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword }); 
} 

vez que conozca los MemberInfo s (me.Member en el anterior), la construcción de sus propios lambdas para el acceso individual debe ser trivial. Por ejemplo (incluyendo un yeso para object para obtener una sola firma):

var param = Expression.Parameter(typeof(T), "x"); 
var memberAccess = Expression.MakeMemberAccess(param, me.Member); 
var body = Expression.Convert(memberAccess, typeof(object)); 
var lambda = Expression.Lambda<Func<T, object>>(body, param); 
var func = lambda.Compile(); 
+0

oooh, bonita :) –

+0

¿Cuál es la mejor manera de mantener las propiedades seleccionadas para un acceso rápido? ¿Lista ? –

+0

'MemberInfo' estaría bien; 'Func ' (o 'Func ') estaría bien; un 'PropertyDescriptor' estaría bien (especialmente con HyperDescriptor). Funcionará de cualquier manera ... –

1

Aquí es la expresión más corta que puedo llegar a:

public static void IncludeProperties(Expression<Action<IUser>> selectedProperties) 
{ 
    // some logic to store parameter 
} 

public static void S(params object[] props) 
{ 
    // dummy method to get to the params syntax 
} 

[Test] 
public void ParamsTest() 
{ 
    IncludeProperties(u => S(
     u.Id, 
     u.Name 
     )); 

} 
+0

Si usa un tipo anónimo e inicializador (vea mi respuesta actualizada) no necesita el método ficticio. Lo cual es bueno –

+0

Sí, ya lo veo :) –

Cuestiones relacionadas