2010-07-25 12 views
6

Estoy escribiendo un servicio para tomar una colección de objetos de un tipo particular y output its primitive, string, and DateTime types a una cadena en CSV Format. Tengo ambas afirmaciones a continuación trabajando. Encuentro que la versión basada en lambda es mucho más limpia.Usando una expresión lambda para evitar el uso de una "cadena mágica" para especificar una propiedad

magia cadena de versión

string csv = new ToCsvService<DateTime>(objs) 
    .Exclude("Minute") 
    .ChangeName("Millisecond", "Milli") 
    .Format("Date", "d") 
    .ToCsv(); 

vs Lambda Versión

string csv = new ToCsvService<DateTime>(objs) 
    .Exclude(p => p.Minute) 
    .ChangeName(p => p.Millisecond, "Milli") 
    .Format(p => p.Date, "d") 
    .ToCsv(); 

recomendación por Jon Skeet todos los métodos lambda comparten un método de firma similares

public IToCsvService<T> Exclude<TResult>(
     Expression<Func<T, TResult>> expression) 

Luego paso el expression.Body al FindMemberExpression. He adaptado código del FindMemberExpression method of ExpressionProcessor.cs del nhlambdaextensions project. Mi versión muy similar de FindMemberExpression es el siguiente:

private string FindMemberExpression(Expression expression) 
{ 
    if (expression is MemberExpression) 
    { 
     MemberExpression memberExpression = (MemberExpression)expression; 

     if (memberExpression.Expression.NodeType == ExpressionType.MemberAccess 
      || memberExpression.Expression.NodeType == ExpressionType.Call) 
     { 
      if (memberExpression.Member.DeclaringType.IsGenericType 
       && memberExpression.Member.DeclaringType 
       .GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
      { 
       if ("Value".Equals(memberExpression.Member.Name)) 
       { 
        return FindMemberExpression(memberExpression.Expression); 
       } 

       return String.Format("{0}.{1}", 
        FindMemberExpression(memberExpression.Expression), 
        memberExpression.Member.Name); 
      } 
     } 
     else 
     { 
      return memberExpression.Member.Name; 
     } 
    } 

    throw new Exception("Could not determine member from " 
     + expression.ToString()); 
} 

estoy probando para bastantes casos en los FindMemberExpression? ¿Es lo que estoy haciendo excesivo dado mi caso de uso?

Respuesta

7

EDIT: El núcleo de hacer esto más sencillo es cambiar la firma de sus métodos a ser genérica en el tipo de resultado demasiado:

public IToCsvService<TSource> Exclude<TResult>(
    Expression<Func<TSource, TResult>> expression) 

De esa manera no va a terminar con una expresión de conversión porque hay la conversión será necesaria. Por ejemplo, p => p.Minute terminará como Expression<Func<DateTime, int>> automáticamente debido a la inferencia de tipo.


Parece una exageración para mí, dado que en este momento todo lo que necesita es una propiedad - por lo menos, eso es todo lo que sus programas de ejemplo.

¿Por qué no comenzar simplemente reconociendo una propiedad y expandirla más tarde si necesita?

EDIT: He aquí una breve pero completa ejemplo que no muestra ninguna conversión:

using System; 
using System.Linq.Expressions; 

class Test 
{ 
    static void Main() 
    { 
     Expression<Func<DateTime, int>> dt = p => p.Minute; 
     Console.WriteLine(dt); 
    } 
} 

Si cambia el tipo de expresión a Expression<Func<DateTime, long>> sin embargo, qué muestran el bit Convert(...). Sospecho que necesita cambiar las firmas de sus métodos Exclude (etc.).

+3

OT: yowser; alrededor de 4 días a partir de 200 k? –

+0

Para reconocer una propiedad solo estaba usando la ruta 'UnaryExpression' y una versión simplificada de la ruta' MemberExpression'. ¿Hay un mecanismo aún más limpio que el que he esbozado? – ahsteele

+0

@Marc: De hecho, 3 si tengo mucha suerte :) –

4

¿Tiene algún plan para hacerlo más flexible, o esto es todo lo que tiene que hacer?

Idealmente, debe tener el código más simple que hará lo que necesita, para que pueda disminuir el número de cosas que pueden salir mal.

Si está haciendo esto como una prueba de concepto, y sabe que más tarde necesitará expresiones lambda, entonces tiene sentido mantenerlas allí, pero, si este es el producto final, entonces el anterior es más fácil de leer , y es menos probable que cause confusión si alguien más necesita hacer cambios en el código.

+0

Estoy a favor de la legibilidad, pero ¿no es el tipo de seguridad lo que vale el "costo" de utilizar una expresión lambda? – ahsteele

+1

@ahsteele - Está produciendo esto en una cadena de todos modos, así que, a menos que esté haciendo algo con los valores, además de escribir en un archivo, escriba-seguridad no hace nada por usted. Además, si le preocupa la seguridad de los tipos, existen otras formas además de agregar la complejidad de las expresiones lambda para obtener eso. Mantenerlo tan simple como debe ser es la parte importante aquí. –

+1

estuvo de acuerdo, pero estaba hablando sobre el tipo de seguridad para las clases contra las que estoy trabajando. Para los fines de este ejemplo, utilicé DateTime, pero si estaba usando un tipo de valor que controlaba y cambiaba un nombre de propiedad, la expresión lambda me ayudaría con eso. Mientras que la versión de la cadena mágica me dejaría en la oscuridad hasta que descubrí el problema. Tiendo a * hacer lo más simple que podría funcionar * pero me pregunto si esa es la respuesta "correcta" aquí. ¿Me estoy perdiendo de algo? – ahsteele

Cuestiones relacionadas