При написании некоторого особенно сложного кода обработки исключений кто-то спросил, разве Вы не должны удостоверяться, что Ваш объект исключения не является пустым? И я сказал, конечно, не, но тогда решил попробовать его. По-видимому, можно бросить пустой указатель, но он все еще превращен в исключение где-нибудь.
Почему это позволяется?
throw null;
В этом отрывке, к счастью 'исключая' не является пустым, но это могло когда-либо быть?
try
{
throw null;
}
catch (Exception ex)
{
//can ex ever be null?
//thankfully, it isn't null, but is
//ex is System.NullReferenceException
}
Потому что спецификация языка ожидает выражения типа System.Exception
(следовательно, null
является допустимым в этом контексте) и не ограничивает это выражение ненулевым значением. В общем, невозможно определить, является ли значение этого выражения нулевым
или нет. Это должно решить проблему остановки. Среде выполнения все равно придется иметь дело со случаем null
. См .:
Exception ex = null;
if (conditionThatDependsOnSomeInput)
ex = new Exception();
throw ex;
Они, конечно, могли бы сделать конкретный случай выброса литерала null
недействительным, но это не сильно помогло бы, так зачем тратить пространство спецификации и уменьшать согласованность ради небольшой выгоды?
Заявление об ограничении ответственности (до того, как меня ударит Эрик Липперт): Это мое собственное предположение о причинах этого дизайнерского решения.Конечно, я не был на совещании по проектированию;)
Ответ на ваш второй вопрос, может ли переменная выражения, пойманная в предложении catch, когда-либо иметь значение NULL: хотя в спецификации C # ничего не говорится о том, могут ли другие языки вызывать исключение null
, которое будет распространяться, оно действительно определяет способ распространения исключений:
Пункты catch, если таковые имеются, проверяются в порядке появления, чтобы найти подходящий обработчик исключения. Первое предложение catch , которое определяет тип исключения или базовый тип типа исключения , считается совпадением. Общее предложение catch считается подходящим для любого типа исключения. [...]
Для null
жирным шрифтом утверждение является ложным. Итак, хотя чисто на основании того, что говорится в спецификации C #, мы не можем сказать, что базовая среда выполнения никогда не будет генерировать null, мы можем быть уверены, что даже в этом случае это будет обрабатываться только общим предложение catch {}
.
Для реализации C # в CLI мы можем обратиться к спецификации ECMA 335. Этот документ определяет все исключения, которые CLI генерирует внутренне (ни одно из которых не является null
), и упоминает, что определенные пользователем объекты исключений генерируются инструкцией throw
. Описание этой инструкции практически идентично инструкции C # throw
(за исключением того, что она не ограничивает тип объекта System.Exception
):
Описание:
Инструкция
throw
создает объект исключения (типO
) в стеке и очищает стек.Подробные сведения о механизме исключений см. В Разделе I.
[Примечание: CLI разрешает создание любого объекта, но CLS описывает конкретный класс исключения, который должен использоваться для взаимодействия языков. конец примечания]Исключения:
System.NullReferenceException
выдается, еслиobj
имеет значениеnull
.Корректность:
Правильный CIL гарантирует, что объект всегда либо
null
, либо ссылка на объект (т. Е. ТипаO
).
Я считаю, что этого достаточно, чтобы сделать вывод, что пойманные исключения никогда не нулевые
.
Взято из здесь :
Если вы используете это выражение в вашем C# код, который он бросит NullReferenceException. То есть потому что для выброса необходимо объект типа Исключение как единственный параметр. Но именно этот объект ноль в моем примере.
Я думаю, что, возможно, вы не можете -- когда вы пытаетесь бросить ноль, он не может, поэтому он делает то, что должен в случае ошибки, то есть бросает исключение по нулевой ссылке. Таким образом, вы на самом деле не бросаете ноль, вы не можете бросить ноль, что приводит к бросанию.
По-видимому, вы можете выбросить null, но это все равно где-то превращается в исключение.
Попытка выбросить объект null
приводит к (совершенно не связанной) исключительной ситуации с нулевой ссылкой.
Спросить, почему вам разрешено бросать null
, все равно что спросить, почему вам разрешено это делать:
object o = null;
o.ToString();
Попытка ответить «… слава богу, ex не является нулевым, но может ли оно когда-либо быть?»:
Поскольку мы, возможно, не можем создавать исключения имеет значение null, предложению catch также никогда не придется перехватывать исключение с нулевым значением. Таким образом, ex никогда не может быть нулевым.
Помните, что исключение включает в себя подробную информацию о том, куда выбрасывается исключение. Видя, что конструктор понятия не имеет, куда бросать, тогда имеет смысл только то, что метод броска впрыскивает эти детали в объект в точке, в которой бросают. Другими словами, CLR пытается ввести данные в ноль, что инициирует NullReferenceException.
Не уверен, что это именно то, что происходит, но это объясняет явление.
Предположив, что это правда (и я не могу придумать лучшего способа получить ex, чем бросить ноль;), это будет означать, что ex не может быть нулем.