Я обнаружил, что смотрю на код, который выглядит примерно так:
private double? _foo;
public double? Foo
{
get { return _foo; }
set { Baz(-value); }
}
public void Baz(double? value)
{
// Perform stuff with value, including null checks
}
И в тесте у меня было следующее:
[Test]
public void Foo_test()
{
...
Foo = null;
...
}
Я ожидал, что тест завершится неудачно (выброс), так как я думал что вы действительно не можете применить унарный минус к null
, что, как я полагал, произойдет в сеттере Foo
. Однако случилось непредвиденное, и тест не провалился .
Я, вероятно, мог бы сделать обоснованное предположение относительно того, почему это происходит, но я решил, что было бы лучше, если бы кто-то, кто действительно знает, что происходит, может просветить меня.
(И прежде, чем кто-то укажет на очевидное: да, отрицание определенно должно быть втиснуто внутрь Baz
.)
Edit and follow up
Итак, я благодарен за вклад Джона Скита и ДжаредПара, которые оба ответили на часть моего вопроса Как . После некоторого поиска в Google я нашел это сообщение в блоге Эрика Липперта, объясняющее математическое определение «поднятого» , которое имеет смысл. Я также прочитал соответствующие части спецификации языка, которые довольно просто объясняют механику подъема в C #.
В частности, теперь я знаю, что в начале процесса подъема операнда выполняется проверка на нуль, и если операнд равен нулю, нуль будет результатом применения оператора (в моем случае унарный минус). Итак, -null
равно null
.
Теперь к части вопроса «Почему». Если я определю свой собственный (унарный) оператор -
в классе
(в котором я инвертирую значение в оболочке), и вызовите этот оператор для нулевой ссылки, я получу NullReferenceException
. Почему «подъем» ведет себя иначе? На самом деле меня интересуют причины этого дизайнерского решения. Я определенно не против этого решения.