2010-06-23 11 views
8

¿El operador ?? en C# usa un cortocircuito al evaluar?¿El operador `` ?? `` usa el cortocircuito?

var result = myObject ?? ExpressionWithSideEffects(); 

Cuando myObject es no nulo, el resultado de ExpressionWithSideEffects() no se utiliza, pero se omite por completo ExpressionWithSideEffects() ser?

Respuesta

7

Sí lo hace. Como siempre, la especificación del lenguaje C# es la fuente definitiva .

A partir de la especificación C# 3, sección 7.12 (V3 en lugar de 4, como la especificación v4 entra en detalles dinámicos que no son realmente relevante aquí):

El tipo de la expresión a ?? b depende de qué las conversiones implícitas están disponibles entre los tipos de operandos. En orden de preferencia, ¿el tipo de a? b es A0, A o B, donde A es el tipo de a, B es el tipo de b (siempre que b tenga un tipo), y A0 es el tipo subyacente de A si A es un tipo anulable, o A de otro modo . Específicamente, a ?? b se procesa como sigue:

  • Si A no es un tipo anulable o un tipo de referencia, un error de tiempo de compilación ocurre.
  • Si A es un tipo anulable y existe una conversión implícita de b a A0, el tipo de resultado es A0. En en tiempo de ejecución, a se evalúa primero. Si un no es nulo, se desenvuelve a tipo A0, y este es el resultado. De lo contrario, b se evalúa y convertido a tipo A0, y esto se convierte en el resultado.
  • De lo contrario, si existe una conversión implícita de b a A, el tipo de resultado es A. En tiempo de ejecución, a se evalúa primero. Si a no es nulo, a se convierte en el resultado . De lo contrario, b se evalúa y convertido a tipo A, y esto se convierte en como resultado.
  • De lo contrario, si b tiene un tipo B y existe una conversión implícita de A0 a B, el tipo de resultado es B. En tiempo de ejecución, se evalúa primero a a. Si a no es nulo, a se desenvuelve al tipo A0 (a menos que A y A0 sean del mismo tipo) y se convierta en tipo B, y este se convierte en el resultado. De lo contrario, b es evaluado y se convierte en el resultado.
  • De lo contrario, a y b son incompatibles y se produce un error en tiempo de compilación.

La segunda, tercera y cuarta viñetas son las relevantes.


Hay una discusión filosófica que se tenía acerca de si el compilador que se esté usando es el real fuente de verdad ... es la verdad acerca de un lenguaje lo que es significaba hacer o lo que es actualmente hace?

+0

A la nota ... Creo que es por eso que todos disfrutamos que Eric Lippert esté cerca :) –

+1

@Matthew: Una de las muchas razones, sí. Un aspecto interesante de Eric es que puede actuar como la encarnación humana de las especificaciones * y * del compilador ... –

10

Sí, hace cortocircuito.

He aquí un fragmento de probar en LINQPad:

string bar = "lol"; 
string foo = bar ?? string.Format("{2}", 1); 
foo.Dump(); 
bar = null; 
foo = bar ?? string.Format("{2}", 1); 
foo.Dump(); 

La primera se unen funciona sin lanzar una excepción mientras que el segundo lo hace de proyección (la cadena de formato no es válido).

+0

mierda, ¡puedo sentirme arrastrado al horizonte de sucesos! – Will

0

Es por eso que tenemos pruebas unitarias.

[TestMethod] 
    public void ShortCircuitNullCoalesceTest() 
    { 
     const string foo = "foo"; 
     var result = foo ?? Bar(); 
     Assert.AreEqual(result, foo); 
    } 

    [TestMethod] 
    [ExpectedException(typeof(ArgumentException))] 
    public void ShortCircuitNullCoalesceFails() 
    { 
     const string foo = null; 
     var result = foo ?? Bar(); 
    } 

    private static string Bar() 
    { 
     throw new ArgumentException("Bar was called"); 
    } 

Estos no son los mejores nombres de prueba, pero se entiende. Muestra que el operador nulo coalesce cortocircuitos como se esperaba.

+0

Y me doy cuenta de que ArgumentException fue una elección extraña, fue solo el primer tipo de excepción que me vino a la mente. – CaffGeek

+3

No es por eso que tenemos pruebas unitarias. Es por eso que tenemos especificaciones de lenguaje. En particular, si tuviéramos pruebas unitarias pero no especificaciones de idioma, solo sabríamos qué sucede en el caso que se está probando. Sin embargo, si tuviéramos la especificación del idioma pero no las pruebas de unidad, aún sabríamos lo que el lenguaje debe hacer en el caso general. Es cierto que las pruebas unitarias ayudan a verificar que el compilador realmente implemente las especificaciones de idioma ... pero siempre prefiero alcanzar la especificación que una prueba unitaria para preguntas como esta. –

+0

@Jon Skeet, touche. Todavía me gusta escribir pruebas rápidas para verificar cosas de las que no estoy seguro. No necesariamente lo mantendré alrededor. Y siempre es posible que el compilador haya implementado las especificaciones incorrectamente ... – CaffGeek

Cuestiones relacionadas