В C# 8, как я могу обнаружить невозможные проверки нуля? - PullRequest
2 голосов
/ 02 апреля 2020

Я начал использовать обнуляемые ссылочные типы в C# 8. До сих пор мне нравится это улучшение, за исключением одной маленькой вещи.

Я переношу старую базу кода, и она заполнена с большим количеством избыточного или недоступного кода, например:

void Blah(SomeClass a) {
  if (a == null) {
    // this should be unreachable, since a is not nullable
  }
}

К сожалению, я не вижу никаких настроек предупреждений, которые могли бы пометить этот код для меня! Было ли это упущением со стороны Microsoft, или я что-то упустил?

Я также использую ReSharper, но ни одна из его настроек предупреждения, похоже, также не фиксирует это. Кто-нибудь еще нашел решение для этого?

Редактировать: Я знаю, что технически это все еще достижимо, потому что проверки обнуляемости не являются пуленепробиваемыми. Дело не в этом. В такой ситуации, когда я объявляю параметр как НЕ обнуляемый , обычно ошибочно проверять, является ли он нулевым. В редких случаях, когда значение null передается как ненулевой тип, я бы предпочел увидеть NullReferenceException и отследить код, вызвавший ошибку, который передал null по ошибке.

1 Ответ

3 голосов
/ 03 апреля 2020

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

Если вы уверены , что все вызывающие абоненты будут используя C# 8 контекст обнуляемости - например, это internal метод - и вы действительно усердно работаете над разрешением всех предупреждений из анализа потока Статина c Рослина (например, вы настроили сервер сборки для обработки их как ошибок) тогда вы правы, что эти нулевые проверки являются избыточными.

Как отмечено в руководстве по миграции , однако, любой внешний код который не использует C# контекст обнуляемости, совершенно не заметит этого:

Новый синтаксис не обеспечивает проверку во время выполнения. Внешний код может обойти анализ потока компилятора.

Учитывая это, как правило, рекомендуется продолжать предоставлять защитные предложения и другие проверки обнуляемости в любых public или protected элементах.

На самом деле, если вы используете пакет Microsoft Code Analysis (который я бы порекомендовал), он предупредит вас использовать пункт охраны в этой конкретной ситуации. По общему признанию, это отчасти потому, что Code Analysis еще не поддерживает nullability . Но даже когда это произойдет, они все еще планируют сохранить это предупреждение на месте из-за вышеуказанных проблем.

Когда вы получите эти предупреждения из анализа кода, вы можете свернуть свой код в нулевая проверка, как вы сделали здесь. Но вы можете и выбросить исключение. На самом деле, вы могли бы бросить еще NullReferenceException - хотя это определенно не рекомендуется. В таком случае вы должны вместо этого бросить ArgumentNullException и передать имя параметра конструктору:

void Blah(SomeClass a) {
  if (a == null) {
    throw new ArgumentNullException(nameof(a));
  }
  …
}

Это гораздо предпочтительнее, чем бросать NullReferenceException в источник, потому что это сообщает вызывающим абонентам, что они могут сделать, чтобы избежать этого сценария, явно назвав точный параметр (в данном случае), который был передан как null. Это более полезно, чем просто получить NullReferenceException - и , возможно * ссылку на ваш внутренний код - где произошло исключение.

Критически, это исключение не должно помочь you debug ваш код - это то, что Code Analysis делает для вас . Вместо этого он демонстрирует, что вы уже определили потенциальную разыменование нулевого значения, и вы составили для него в источнике .

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

void Blah(SomeClass a) {
  _ = a?? throw new ArgumentNullException(nameof(a));
}

Это действительно окольный способ ответить на ваш первоначальный вопрос, а именно, как обнаружить наличие нулевых проверок, сделанных ненужными с помощью C # необнуляемые ссылочные типы.

Короткий ответ: вы не можете; на этом этапе анализ потоков Розелина c направлен на выявление возможности разыменования нулевых объектов, а не на обнаружение потенциально посторонних проверок.

Длинный ответ, хотя, как указано выше, заключается в том, что вы не должна ; до тех пор, пока Microsoft не добавит проверку во время выполнения или не разрешит контекст обнуляемости, эти пустые проверки продолжают давать значение.

...