¿Cómo es esto mejor que el operador Null Coalesce?

Me encontré con este entrada de blog de hoy.

Voy a resumir. El bloguero está comentando este código y dice que es feo.

// var line1 = person.Address.Lines.Line1 ?? string.Empty;
// throws NullReferenceException: 
//    {"Object reference not set to an instance of an object."}

// The ugly alternative

var line1 = person.Address == null
    ? "n/a"
    : person.Address.Lines == null
    ? "n/a"
    : person.Address.Lines.Line1;

El blogger luego escribe una clase que le permite reemplazar el código anterior con una nueva sintaxis.

var line2 = Dis.OrDat<string>(() => person.Address.Lines.Line2, "n/a");

El código de la clase Dis es como sigue.

 public static class Dis
  {
    public static T OrDat<T>(Expression<Func<T>> expr, T dat)
    {
      try
      {
        var func = expr.Compile();
        var result = func.Invoke();
        return result ?? dat; //now we can coalesce
      }
      catch (NullReferenceException)
      {
        return dat;
      }
    }
  }

Entonces, la primera pregunta que tengo es por qué el código original que contiene ?? tiene que ser reemplazado con el aspecto maldito ?: código.

Mi segunda pregunta es ¿por qué usar el árbol de expresión sobre Null Coalesce? La única razón por la que puedo pensar en ello es porque están usando la inicialización diferida, pero eso no está claro en la publicación, ni está claro en el código de prueba, que está en la publicación del blog. Lo publicaré aquí también.

Por cierto, ¿alguien sabe cómo crear una ventana de bloque de código de tamaño fijo? ¿O se prefiere aquí un bloque de código sin desplazamiento? No vi nada en el blog de Meta.

preguntado el 28 de agosto de 11 a las 02:08

Si publica un bloque de código muy largo, aparecerá barras de desplazamiento. Los bloques de código como los de tu publicación me parecen bien. Prefiero usar simplemente la barra de desplazamiento del navegador principal y no tener que usar barras de desplazamiento anidadas. Los bloques de código muy largos probablemente sean demasiado largos. -

@Douglas: Estoy de acuerdo con las barras de desplazamiento principales. Solo estaba verificando la convención adecuada ya que rara vez publico código. -

Gracias por preguntar, su código se ve bien. Si busca una respuesta más detallada, puede intentar meta.stackoverflow.com -

6 Respuestas

No veo ninguna razón para usar árboles de expresión en la muestra de blog, Func es suficiente allí.

Mi sugerencia es utilizar Maybe * monada iplementation, en lugar de ese código. Ver ejemplo.

public static class Maybe
{
    public static TResult With<T, TResult>(this T self, Func<T, TResult> func) where T : class
    {
        if (self != null)
            return func(self);
        return default(TResult);
    }

    public static TResult Return<T, TResult>(this T self, Func<T, TResult> func, TResult result) where T : class
    {
        if (self != null)
            return func(self);
        return result;
    }
}

Y tu código se convierte en:

var line2 = person
   .With(p => p.Address)
   .With(a => a.Lines)
   .Return(l => l.Line2, "n/a");

[*] Esta no es una mónada real, sino una versión muy simplificada de la misma.

respondido 16 mar '15, 23:03

El código del blog es peor. Un nombre de método Dis.OrDat es feo y no describe lo que realmente hace el método.

El uso de un Expression<T> es redundante, podría ser simplemente:

public static T OrDat<T>(Func<T> func, T dat)
{
  try
  {
    return func() ?? dat;
  }
  catch (NullReferenceException)
  {
    return dat;
  }
}

Él llama Compile y Invoke uno tras otro de inmediato, por lo que en realidad no hace nada con el árbol de expresión. Pasando en el Func<T> ya que sería lo mismo, sin la sobrecarga de compilar el Func.

Pero peor aún, el código usa excepciones para el control de flujo, que siempre es malo: el person.Address La propiedad parece ser opcional, por lo que no es "excepcional" que sea nula, por lo que el código que la usa no debería generar una excepción. los catch arriba no puedo distinguir entre person.Address == null y la implementación del Address getter de propiedad se rompe internamente causando un NullReferenceException para ser arrojado. Simplemente se los traga a todos.

Entonces, en general, estaría feliz de ignorar la publicación del blog.

Respondido 28 ago 11, 07:08

+1 Excepciones para el control de flujo. No permita que el tiempo de ejecución arroje excepciones si puede validar los valores usted mismo. - Erik Philips

@Douglas: Nunca he usado un árbol de expresión explícitamente, así que eso me desconcertó. Pero me gusta el recordatorio de no usar excepciones para el control de flujo. Lo he hecho demasiadas veces. - surfasb

Para proteger este código:

var line1 = person.Address.Lines.Line1 ?? string.Empty;

de lanzar NullReferenceException

Yo simplemente usaría:

var line1 = string.Empty;
if ((person.Address != null) && (person.Address.Lines != null))
   line1 = person.Address.Lines.Line1 ?? string.Empty;

en lugar de los Bloggers esto o aquello (Dis.OrDat),

Respondido 28 ago 11, 07:08

Creo que el blogger estaba tratando de evitar una solución como esta porque no parece que se amplíe bien. - surfasb

el problema con la primera línea de código ( var line1 = person.Address.Lines.Line1 ?? string.Empty ) es que arrojará un error si persona, Dirección o Líneas son nulas. El operador de fusión nula solo está trabajando en el resultado de toda la expresión.

Es una solución bastante elegante, pero me gustaría comprobar cómo era el rendimiento de los árboles de expresión antes de comenzar a rociar esto a través de mi código (pero solo porque me mordió el uso excesivo de la reflexión en el pasado antes de saber qué perro era. )

Respondido 28 ago 11, 06:08

Para su primera pregunta, el código en cuestión:

var line1 = person.Address.Lines.Line1 ?? string.Empty;

lanzará un NullReferenceException if person, Addresso Lines is null. El código de reemplazo que usa las declaraciones ternarias if protege contra ese caso. El operador de coalescencia nula solo operará en el Line1 propiedad, por lo que no puede proteger contra el resto de la expresión null.

Para su segunda pregunta, la razón para usar el árbol de expresiones probablemente sea para "simplificar" el código requerido para asegurar que se pueda evaluar la expresión completa. Si bien el código funcionaría, creo que introduce una capa de complejidad y sobrecarga que no es realmente necesaria o necesaria.

Respondido 28 ago 11, 07:08

Para aquellos que miran esta entrada pero usan C # 6.0 (o superior), el código ahora puede usar Propagación nula, y estar escrito de la siguiente manera:

var line1 = person?.Address?.Lines?.Line1 ?? "n/a";

Respondido el 13 de enero de 17 a las 21:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.