¿Es posible utilizar operator? y throw new Exception()?


Tengo una serie de métodos haciendo siguiente:

var result = command.ExecuteScalar() as Int32?;
if(result.HasValue)
{
   return result.Value;
}
else
{
   throw new Exception(); // just an example, in my code I throw my own exception
}

Desearía poder usar el operador ?? así:

return command.ExecuteScalar() as Int32? ?? throw new Exception();

Pero genera un error de compilación.

¿Es posible reescribir mi código o solo hay una forma de hacerlo?

Author: user1069816, 2009-11-19

5 answers

Para C# 7

En C# 7, throw se convierte en una expresión, por lo que está bien usar exactamente el código descrito en la pregunta.

Para C # 6 y anteriores

No puedes hacer eso directamente en C # 6 y anteriores - el segundo operando de ?? tiene que ser una expresión, no una declaración de lanzamiento.

Hay algunas alternativas si realmente solo estás tratando de encontrar una opción que sea concisa:

Podrías escribir:

public static T ThrowException<T>()
{
    throw new Exception(); // Could pass this in
}

Y entonces:

return command.ExecuteScalar() as int? ?? ThrowException<int?>();

Yo realmente no recomiendo que hagas eso... es bastante horrible y uniomático.

Qué tal un método de extensión:

public static T ThrowIfNull(this T value)
{
    if (value == null)
    {
        throw new Exception(); // Use a better exception of course
    }
    return value;
}

Entonces:

return (command.ExecuteScalar() as int?).ThrowIfNull();

Otra alternativa (de nuevo un método de extensión):

public static T? CastOrThrow<T>(this object x) 
    where T : struct
{
    T? ret = x as T?;
    if (ret == null)
    {
        throw new Exception(); // Again, get a better exception
    }
    return ret;
}

Llamar con:

return command.ExecuteScalar().CastOrThrow<int>();

Es algo feo porque no se puede especificar int? como argumento de tipo...

 51
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-24 17:11:09

Como se ha dicho, no se puede hacer esto con el ?? operador (bueno, no sin algunas contorsiones que no parecen encajar con su objetivo de hacer este limpiador).

Cuando veo emerger este patrón, inmediatamente pienso en Las medidas de ejecución. Originalmente del mundo de C++ se transfieren a C# bastante bien, aunque posiblemente sean menos importantes la mayor parte del tiempo.

La idea es que tomes algo de la forma:

if( condition )
{
  throw Exception;
}

Y lo convierte a:

Enforce<Exception>( condition );

(puedes simplifique aún más mediante la omisión del tipo de excepción).

Yendo más lejos, puede escribir un conjunto de métodos estilo Nunit para diferentes comprobaciones de condición, por ejemplo;

Enforce<Exception>.NotNull( obj );
Enforce<Exception>.Equal( actual, expected );
Enforce<Exception>.NotEqual( actual, expected );

Etc.

O, mejor aún, proporcionando una expectativa lamba:

Enforce<Exception>( actual, expectation );

Lo que es realmente bueno es que, una vez que haya hecho eso, puede devolver el actual param y hacer cumplir inline :

return Enforce( command.ExecuteScalar() as Int32?, (o) => o.HasValue ).Value;

... y esto parece ser lo más cercano a lo que buscas.

He llamado una implementación de esto antes. Hay un par de pequeños inconvenientes, como la forma genéricamente crear un objeto de excepción que toma argumentos-algunas opciones allí (elegí reflexión en el momento, pero pasar una fábrica como un parámetro adicional puede ser aún mejor). Pero en general todo es bastante sencillo y realmente puede limpiar una gran cantidad de código.

Está en mi lista de cosas que hacer para crear una implementación de código abierto.

 9
Author: philsquared,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-19 13:24:49

Si solo desea una excepción cuando el valor devuelto no es un Int32 entonces haga esto:

return (int)command.ExecuteScalar();

Si quieres lanzar tu propia excepción personalizada entonces probablemente haría algo como esto en su lugar:

int? result = command.ExecuteScalar() as int?;
if (result == null) throw new YourCustomException();
return result.Value;
 4
Author: LukeH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-19 12:23:46

No vas a poder lanzar una excepción en el lado derecho del operador coalescente nulo. La razón detrás de esto es que el lado derecho del operador necesita ser una expresión, no una declaración.

El operador coalescente null funciona así: si el valor izquierdo del operador es null, devuélvalo; de lo contrario, devuelve lo que está a la derecha del operador. La palabra clave throw no devuelve un valor; por lo tanto, no se puede usar en el lado derecho del operador.

 2
Author: Adam Maras,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-19 11:54:19

La razón por la que no puedes hacer:

return command.ExecuteScalar() as Int32? ?? throw new Exception();

Es porque lanzar una excepción es una declaración, no una expresión.

Si solo está buscando acortar el código un poco, tal vez esto:

var result = command.ExecuteScalar() as Int32?;
if(result.HasValue) return result;
throw new Exception();

No hay necesidad de lo demás.

 1
Author: Josh Smeaton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-19 12:01:41