Можно ли использовать оператор ?? и бросить новое исключение ()? - PullRequest
27 голосов
/ 19 ноября 2009

У меня есть несколько методов, которые делают следующее:

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
}

Хотелось бы использовать оператор ?? вот так:

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

но выдает ошибку компиляции.

Можно ли переписать мой код или есть только один способ сделать это?

Ответы [ 5 ]

54 голосов
/ 19 ноября 2009

Для C # 7

В C # 7, throw становится выражением, поэтому можно использовать именно тот код, который описан в вопросе.

Для C # 6 и более ранних версий

Вы не можете сделать это напрямую в C # 6 и ранее - второй операнд ?? должно быть выражением, а не оператором throw.

Есть несколько альтернатив, если вы действительно пытаетесь найти краткий вариант:

Вы можете написать:

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

А потом:

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

Я на самом деле не рекомендую делать это, хотя ... это довольно ужасно и неубедительно.

Как насчет метода расширения:

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

Тогда:

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

Еще одна альтернатива (снова метод расширения):

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;
}

Позвонить по номеру:

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

Это несколько уродливо, потому что вы не можете указать int? в качестве аргумента типа ...

9 голосов
/ 19 ноября 2009

Как уже было сказано, вы не можете сделать это с ?? оператор (ну, не без некоторых искажений, которые, кажется, не вписываются в вашу цель сделать это чище).

Когда я вижу этот паттерн, я сразу думаю о Enforcements . Первоначально из мира C ++ они довольно хорошо переносятся на C #, хотя в большинстве случаев, возможно, менее важны.

Идея состоит в том, что вы берете что-то в форме:

if( condition )
{
  throw Exception;
}

и преобразует его в:

Enforce<Exception>( condition );

(вы можете еще больше упростить, указав тип исключения по умолчанию).

Если продолжить, вы можете написать набор методов в стиле Nunit для различных проверок условий, например;

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

и т.д.

Или, еще лучше, предоставляя ожидание ламба:

Enforce<Exception>( actual, expectation );

Что действительно хорошо, так это то, что, как только вы это сделаете, вы можете вернуть фактический параметр и применить inline :

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

... и это, кажется, ближе всего к тому, что вы ищете.

Я уже реализовал это раньше. Есть пара небольших недоразумений, например, как вы обычно создаете объект исключения, который принимает аргументы - некоторые варианты там (я выбрал отражение в то время, но передача фабрики в качестве дополнительного параметра может быть даже лучше). Но в целом все довольно просто и может действительно убрать много кода.

Это в моем списке того, что нужно сделать, чтобы запустить реализацию с открытым исходным кодом.

4 голосов
/ 19 ноября 2009

Если вы просто хотите исключение, когда возвращаемое значение не является Int32, то сделайте следующее:

return (int)command.ExecuteScalar();

Если вы хотите создать свое собственное исключение, я бы вместо этого сделал что-то вроде этого:

int? result = command.ExecuteScalar() as int?;
if (result == null) throw new YourCustomException();
return result.Value;
2 голосов
/ 19 ноября 2009

Вы не сможете создать исключение в правой части оператора объединения нулей. Причина этого заключается в том, что правая часть оператора должна быть выражением, а не выражением.

Оператор объединения нулей работает следующим образом: если левое значение оператора равно нулю, вернуть его; в противном случае верните то, что находится справа от оператора. Ключевое слово throw не возвращает значение; следовательно, его нельзя использовать справа от оператора.

1 голос
/ 19 ноября 2009

Причина, по которой вы не можете сделать:

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

Это потому, что выбрасывание исключения является оператором, а не выражением.

Если вы просто хотите немного сократить код, возможно, это:

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

Остальное не нужно.

...