2010-12-09 13 views
5

feo:¿Quizás la mónada usa árboles de expresión?

string city = null; 
if (myOrder != null && myOrder.Customer != null) 
    city = myOrder.Customer.City; 

mejor (maybe monad):

var city = myOrder 
       .With(x => x.Customer) 
       .With(x => x.City) 

aún mejor? ¿Alguna razón por la cual esto no pudo ser escrito?

var city = Maybe(() => myOrder.Customer.City); 

Respuesta

3

Sí, debería ser posible. Sin embargo, es bastante más complicado de lo que parece en la superficie implementar una nueva escritura del árbol de expresiones correctamente. Especialmente si desea poder manejar correctamente campos, propiedades, propiedades indexadas, llamadas a métodos y otras construcciones que son válidas en expresiones arbitrarias.

Tampoco es la operación que mejor funciona, ya que para evaluar la expresión tiene que compilar dinámicamente el árbol de expresiones en una función lambda cada vez.

Hay implementation on this pattern on CodePlex. Nunca lo he usado personalmente, así que no puedo decir qué tan bien implementado es, o si maneja todos los casos que he descrito.

Una alternativa a la creación de una expresión árbol de re-escritor, es escribir Maybe() para aceptar una función lambda (en lugar de un árbol de expresión) y la captura de cualquier ArgumentNullException tirado, volviendo default(T) en esos casos. Frota a muchas personas de la manera incorrecta para usar excepciones para el control de flujo de esta manera ... pero ciertamente es una implementación más sencilla para hacerlo bien. Personalmente, lo evito porque puede enmascarar errores de referencia nulos dentro de métodos llamados como parte de la expresión, lo cual no es deseable. respuesta

0

más simple si los objetos son baratos a crear y que quieren evitar los cheques nulos:

myOrder.NewIfNull().Customer.NewIfNull().City; 

Esto devolverá null o algún valor inicial se establece en el constructor o el campo inicializador para la ciudad. NewIfNull no está incorporado, pero es muy fácil:

public static T NewIfNull<T>(this T input) where T:new() 
{ 
    return input ?? new T(); 
} 
+0

La creación de nuevos objetos puede tener consecuencias imprevistas. – Amy

0

Yo sé que mi aplicación de Tal vez (según el artículo CodeProject) tiene un costo, pero estoy seguro de que es nada en comparación con la idea de conseguir un Expression<T> involucrado allí. Básicamente estás hablando Reflexión todo el camino. No me importaría si estuviera precompilado, al estilo de Roslyn, pero todavía no hemos llegado.

Yo diría que la ventaja de mi implementación va más allá de lo mítico. operador. La capacidad de escribir un algoritmo completo utilizando una cadena como esta significa que puede inyectar sus propias creaciones (como If, Do, etc.) y proporcionar su propia lógica especializada.

Me doy cuenta de que esto es más complicado de lo que estamos tratando de hacer aquí, pero no parece que vayamos a obtener un operador de punto de fusión nula en C# 5.

1

Algunos puntos que vienen a la mente:

  • .Solutions funcionan bien para los objetos de memoria, pero se meten en problemas con EF ya que estas llamadas estáticas no pueden ser convertidos para funcionar en contra de almacenamiento persistente (es decir, SQL DB). Esto limita un poco el alcance de la aplicación.

  • Prácticamente siempre quiero saber si la cadena produjo un resultado válido. Por lo tanto, tendré un bloque condicional if(city == null) en cualquier caso.

  • Cualquier solución actual que no sea "lo feo" implica Expresiones.

Por lo tanto, mi elección sería algo así como

var property = (() => myOrder.Customer.City); 
city = HasValue(property) ? property.Invoke() : "unknown"; 

HasValue(Expression e) paseos por el árbol de expresión LINQ recursivamente hasta que alcance el final (volviendo true) o se encuentra con la propiedad nulo valorada (volviendo falsa). La implementación debe ser simple, use MethodInfo Member de la clase MemberExpression para analizar el AST. También se podría implementar getter de esta manera como Brian sugirió, pero me gusta más arriba porque HasValue siempre devuelve bool. Además:

  • También se pueden gestionar invitaciones de miembros.
  • La evaluación podría hacerse como myOrder.HasValue(x => x.Customer.City), pero eso trae algunas complicaciones.
Cuestiones relacionadas