Этот вопрос задал один из членов нашей команды в процессе программирования. Поскольку этой ошибки компиляции легко избежать, я никогда не задумывался об этой проблеме внимательно, пока не посмотрел на его код и не понял, что эта проблема не так уж и проста.
Сначала взгляните на этот код:
код
классная программа
{
static void Main(string[] args)
{
байт[] buf = новый байт[1024];
Т т = новый Т();
строка ул = "1234";
интервал n = 1234;
интервал?nn = 1234;
ДатаВремя dt = ДатаВремя.Сейчас;
объект о = 1234;
Console.WriteLine("Готово");
}
}
класс Т { }
Как вы думаете, сколько переменных не используется в этом коде?
Если вы посмотрите на это с точки зрения программиста, ответом будет то, что все переменные не используются. Но результат, выдаваемый компилятором, немного противоречив:
Переменной «str» присвоено значение, но ее значение ни разу не использовалось. Переменной «n» присвоено значение, но ее значение ни разу не было присвоено. значение никогда не использовалось.
Странно то, что хотя все переменные объявлены одинаково, компилятор думает лишь о том, что некоторые из них не используются. Что происходит?
Давайте проанализируем их один за другим. Сначала посмотрите на массив. Если используется значение по умолчанию, информация, предоставляемая компилятором, отличается:
byte[] buf1 = null // Есть предупреждение;
byte[] buf2 = новый байт[1024] // нет предупреждения;
Этот результат, по-видимому, указывает на то, что если присвоение параметра равно нулю, компилятор фактически не будет выполнять присвоение, и переменная будет обрабатываться так, как если бы она не использовалась. Результаты проверки IL также могут подтвердить это утверждение: для первой строки компилятор не сгенерировал никакого соответствующего оператора, для второй строки для создания массива использовалась инструкция newattr;
Для пользовательских классов:
T t1 = null // Есть предупреждение;
T t2 = новый T(); // нет предупреждения
Этот результат должен быть понятен (хотя он и понятен, но я не считаю его хорошим по причинам, указанным ниже). Хотя мы не вызываем никаких методов класса, конструктор класса все равно может выполнять некоторые операции, поэтому, пока класс создается, компилятор будет обрабатывать его так, как если бы он уже использовался.
Для базовых типов значений их поведение отличается от ссылочных типов. Компилятор не рассматривает первоначальное присвоение как использование переменных:
int n1 = 0 // Есть предупреждение
int n2 = 1234 // Есть предупреждение;
int? n3 = null // Есть предупреждение;
int? n4 = 0 // Есть предупреждение
int? n5 = 1234 // Есть предупреждение;
Строку следует рассматривать как ссылочный тип с точки зрения реализации, но ее производительность больше похожа на тип значения, а предупреждающая информация такая же, как и у типа значения.
Для немного более сложных типов значений результаты немного более тонкие:
DateTime dt1 // Есть предупреждение.
DateTime dt2 = new DateTime() // Есть предупреждение.
DateTime dt3 = new DateTime(2009,1,1 // без предупреждения);
DateTime dt4 = DateTime.Now // нет предупреждения;
В отношении этого результата следует отметить одну вещь. Хотя конструктор DateTime по умолчанию и параметризованный конструктор являются конструкторами с точки зрения пользователя, они отличаются с точки зрения компилятора. При декомпиляции с помощью IL также можно увидеть, что при вызове конструктора по умолчанию компилятор вызывает инструкцию initobj, а инструкция вызова ctor используется для параметризованного конструктора. Кроме того, хотя формат кода присваивания с точки зрения программиста абсолютно одинаков, компилятор будет применять разные стратегии построения в зависимости от присвоенного значения, что также противоречит здравому смыслу.
Окончательный вывод вызывает сожаление: предупреждений компиляции C# недостаточно, чтобы обеспечить программистам достаточную защиту, особенно для массивов:
байт[] buf = новый байт[1024];
Если вы создадите такой массив, не используя его, компилятор не выдаст программисту никакого предупреждающего сообщения.
Еще одна проблема, которую стоит рассмотреть, — это объявление класса без использования каких-либо методов, например просто
Т т = новый Т()
Это разумное поведение? Должен ли компилятор выдать предупреждение об этом?
Мое личное мнение таково, что с точки зрения использования это неразумно, и его следует избегать, насколько это возможно. Компилятор должен выдать предупреждение, если обнаружит такое использование. При необходимости вы можете избежать предупреждающих сообщений, специально объявив их с помощью директив компиляции или методов атрибутов. Однако компилятор C# не выдает предупреждений, с чем я не согласен. Конечно, я также надеюсь, что каждый выдвинет свои идеи.