Подтвердить что ты не робот

Почему С# позволяет вам "выбросить нуль"?

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

Почему это разрешено?

throw null;

В этом фрагменте, к счастью, "ex" не является нулевым, но может ли он когда-либо быть?

try
{
  throw null;
}
catch (Exception ex)
{
  //can ex ever be null?

  //thankfully, it isn't null, but is
  //ex is System.NullReferenceException
}
4b9b3361

Ответ 1

Поскольку спецификация языка ожидает выражения типа System.Exception (поэтому null является допустимым в этом контексте) и не ограничивает это выражение не равным нулю. В общем, нет способа определить, является ли значение этого выражения null или нет. Это должно было бы решить проблему прекращения. В любом случае время выполнения будет иметь дело с событием null. См:

Exception ex = null;
if (conditionThatDependsOnSomeInput) 
    ex = new Exception();
throw ex; 

Они могли бы, конечно, сделать конкретный случай выброса литерала null недействительным, но это не помогло бы, поэтому зачем тратить пространство спецификации и уменьшать согласованность для небольшой выгоды?

Отказ от ответственности (до того, как меня ударит Эрик Липперт): Это мое собственное предположение о рассуждениях, стоящих за этим проектным решением. Конечно, я не был на совещании по дизайну;)


Ответ на ваш второй вопрос: может ли переменная выражения, попавшая в предложение catch, быть нулевой: хотя спецификация С# не говорит о том, могут ли другие языки генерировать исключение null, оно определяет способ исключения распространяются:

Оговорки catch, если они есть, рассматриваются в порядке появления, чтобы найти подходящий обработчик для исключения. Первое предложение catch , которое указывает тип исключения или базовый тип типа исключения, считается совпадением. Общее предложение catch считается совпадением для любого типа исключения. [...]

Для null полужирный оператор false. Таким образом, хотя чисто основано на том, что говорит спецификация С#, мы не можем сказать, что базовая среда выполнения никогда не будет показывать нуль, мы можем быть уверены, что даже если это произойдет, она будет обрабатываться только общим catch {} пункт.

Для реализации С# в CLI мы можем обратиться к спецификации ECMA 335. Этот документ определяет все исключения, которые CLI бросает внутри (ни один из которых не является null), и упоминает, что пользовательские объекты исключения генерируются командой throw. Описание этой инструкции практически идентично оператору С# throw (за исключением того, что он не ограничивает тип объекта System.Exception):

Описание:

Команда throw выдает объект исключения (тип O) в стек и освобождает стек. Подробнее о механизме исключения см. В разделе I.
[Примечание. Хотя CLI разрешает бросать любой объект, CLS описывает конкретный класс исключений, который должен использоваться для взаимодействия языков. end note]

Исключения:

System.NullReferenceException вызывается, если obj null.

Корректность:

Правильный CIL гарантирует, что объект всегда либо null, либо ссылка на объект (т.е. типа O).

Я считаю, что их достаточно, чтобы заключить пойманные исключения, никогда null.

Ответ 2

По-видимому, вы можете выбросить нуль, но он все равно превращается в исключение.

Попытка выбросить объект null приводит к (полностью несвязанному) Null Reference Exception.

Вопрос о том, почему вам разрешено бросать null, - это вопрос, почему вы можете это сделать:

object o = null;
o.ToString();

Ответ 3

В то время как невозможно сбросить null в С#, потому что бросок обнаружит это и превратит его в исключение NullReferenceException, возможно получить null... Я получаю это прямо сейчас, что вызывает мой catch ( который не ожидал, что "ex" будет нулевым), чтобы получить исключение нулевой ссылки, которое затем приводит к тому, что мое приложение умирает (поскольку это был последний улов).

Итак, хотя мы не можем выбросить null из С#, netherworld может выбросить нуль, поэтому ваш внешний вид (Exception ex) лучше подготовлен к его получению. Просто FYI.

Ответ 4

Взято из здесь:

Если вы используете это выражение в своем С# кода он будет NullReferenceException. То есть потому что запрос-запрос требует объект типа Exception как его единственный параметр. Но этот самый объект null в моем примере.

Ответ 5

Я думаю, может быть, вы не можете - когда вы пытаетесь выбросить нуль, он не может, поэтому он делает то, что должен, в случае ошибки, в котором выбрано исключение с нулевой ссылкой. Таким образом, вы на самом деле не выбрасываете нуль, вы не можете выбросить нуль, что приводит к броску.

Ответ 6

Попытка ответить: ".." ex "не является нулевым, но может ли быть когда-нибудь?":

Так как мы, возможно, не можем генерировать исключения, которые являются нулевыми, предложение catch также никогда не должно поймать исключение, которое равно null. Таким образом, ex никогда не может быть нулевым.

Теперь я вижу, что этот вопрос уже был спросил.

Ответ 7

Помните, что исключение содержит сведения о том, где генерируется исключение. Увидев, что конструктор понятия не имеет, куда его нужно бросить, тогда имеет смысл только, что метод throw вводит эти детали в объект в точке броска. Другими словами, CLR пытается ввести данные в null, что вызывает исключение NullReferenceException.

Не уверен, что это именно то, что происходит, но объясняет это явление.

Предполагая, что это правда (и я не могу думать, что лучший способ заставить ex быть нулевым, чем throw null;), это означает, что ex не может быть null.