Как синтаксический сахар может сыграть с вами злую шутку

от автора

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

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

Синтаксический сахар в C#

В нашей команде используется язык C#, поэтому мой рассказ будет про его синтаксис.

Синтаксический сахар заменяет длинные и часто используемые конструкции кода, делает их более короткими и быстро читаемыми, облегчает и ускоряет разработку.

Здесь стоит отметить и другое мнение: существуют люди, которые считают, что синтаксический сахар только усложняет код, делая его менее читаемым.

Я придерживаюсь данного мной определения, хотя опыт показал, что не всё так однозначно. Разберем на примерах:

Примеры:

  • Конструкция new () {1, 2} заменяет new List() {1, 2}

  • Оператор += заменяет конструкцию

    • number += 1 =>

    • number = number + 1

  • А оператор ??= вообще имеет долгую историю

    • foo ??= "Строка была равна null" =>

    • foo = foo ?? "Строка была равна null" =>

    • foo = foo is null ? "Строка была равна null" : foo =>

    • if (foo is null)
      {
      foo = "Строка была равна null";
      }

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

Неожиданные Null Reference Exception

Итак, перейдем к истории. Всё началось с того момента, когда в нашем проекте начали «выстреливать» ошибки ... var result = new ExampleClass { ExString = "Тут не должно быть взрыва", ExList = { 0, 1 }, ExString2 = "И тут", ExInt = 24 }; ...

public class ExampleClass { public string? ExString { get; set; } public List<int>? ExList { get; set; } public string? ExString2 { get; set; } public int ExInt { get; set; } }

Наверное, я оставил очень прозрачные подсказки в коде, чтобы вы смогли догадаться, из-за чего выбрасывалась ошибка. Но в моменте было совсем не ясно, особенно когда ты пытаешься быстро разобраться в ситуации из-за ошибок на продакшене.

Пояснение

Можно обратить внимание на весьма странный синтаксис в коде.

... var result = new ExampleClass {     ...     ExList = { 0, 1 },     ... }; ...

Выше я приводил пример того, как конструкция new() {0, 1} заменяет new List() {0, 1}. Можно заметить, что {0, 1} уж очень похоже на new() {0, 1}.
Первый раз, глядя на эту конструкцию, в голову совсем не приходит мысль о том, что тут что-то не так. Скорее возникает мысль: «Наверное, это новый синтаксис». После 10 минут интенсивного просмотра кода было принято решение заменить {0, 1} на new() {0, 1}. И, о чудо, ошибки исчезли.

Но причём тут NRE? А всё дело в том, что{0, 1} заменяет конструкцию .Add() И вот эти два блока кода оказываются идентичными:

// 1 var result1 = new ExampleClass {     ExList = { 1, 2 },     ExString = "Привет",     ExInt = 24 };  // 2 var result2 = new ExampleClass {     ExString = "Привет",     ExInt = 24 }; result.ExList.Add(1); result.ExList.Add(2);

Так как ExList при инициализации равен null, то при попытке вызывать null.Add() мы получаем https://habr.com/ru/articles/764586/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *