Опасны ли неинициализированные переменные C #? - PullRequest
36 голосов
/ 19 января 2012

Я знаком со спецификацией C #, , раздел 5.3 , в которой говорится, что переменная должна быть назначена перед использованием.

В C и неуправляемом C ++ это имеет смысл, так как стек не очищается, и место в памяти, используемое для указателя, может быть где угодно (что приводит к трудному отслеживанию ошибки).

Но у меня сложилось впечатление, что во время выполнения не существует действительно "неназначенных" значений. В частности, ссылочный тип, который не инициализирован, всегда будет иметь нулевое значение, а не значение, оставшееся от предыдущего вызова метода или случайного значения.

Это правильно, или я ошибочно полагал, что проверки на ноль достаточно все эти годы? Можете ли вы иметь полностью неинициализированные переменные в C #, или CLR позаботится об этом, и всегда будет установлено НЕКОТОРЫЕ значения.

Ответы [ 6 ]

61 голосов
/ 20 января 2012

У меня сложилось впечатление, что во время выполнения не существует действительно "неназначенных" значений. В частности, что ссылочный тип, который не инициализирован, всегда будет иметь нулевое значение, а не значение, оставшееся от предыдущего вызова метода или случайного значения. Это правильно?

Замечу, что на ваш вопрос еще никто не ответил.

Ответ на вопрос, который вы на самом деле задали, - "Сорта".

Как уже отмечали другие, некоторые переменные (элементы массива, поля и т. Д.) Классифицируются как автоматически «изначально присвоенные» их значению по умолчанию. (Что является нулевым для ссылочных типов, ноль для числовых типов, false для bools и естественной рекурсии для пользовательских структур).

Некоторые переменные не классифицируются как изначально присвоенные; локальные переменные, в частности, изначально не назначаются. Они должны быть классифицированы компилятором как «точно назначенные» во всех точках, где используются их значения .

Тогда ваш вопрос фактически таков: «является ли локальная переменная, которая классифицируется как , ей определенно не присваивается на самом деле изначально присваивается так же, как поле?» И ответ на этот вопрос да , на практике среда выполнения изначально назначает всех локальных пользователей.

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

Во время выполнения разрешено оставлять исходное состояние местных жителей, как если бы мусор был там, если он может сделать это безопасно. Но, как деталь реализации, он никогда не выбрал это. Он обнуляет память для локальной переменной агрессивно.

Причина, по которой правило о том, что местные жители должны быть однозначно назначены до их использования, это , а не , чтобы вы не могли наблюдать неинициализированное состояние локального мусора. Это уже ненаблюдаемо, потому что CLR агрессивно очищает локальные значения до значений по умолчанию, так же как и для полей и элементов массива. Причина, по которой это недопустимо в C #, заключается в том, что использование неназначенного локального файла имеет высокую вероятность быть ошибкой. Мы просто делаем его недопустимым, и затем компилятор предотвращает возникновение такой ошибки.

9 голосов
/ 19 января 2012

Насколько я знаю, у каждого типа есть назначенное значение по умолчанию.

Согласно этому документу полям классов присваивается значение по умолчанию.

http://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx

В этом документе говорится, что для следующих всегда значения по умолчанию назначаются автоматически.

  • Статические переменные.
  • Переменные экземпляра экземпляров класса.
  • Переменные экземпляра изначально назначенных переменных структуры.
  • Элементы массива.
  • Значения параметров.
  • Справочные параметры.
  • Переменные, объявленные в предложении catch или в выражении foreach.

http://msdn.microsoft.com/en-us/library/aa691173(v=vs.71).aspx

Подробнее о фактических значениях по умолчанию здесь: http://msdn.microsoft.com/en-us/library/83fhsxwc.aspx

3 голосов
/ 19 января 2012

Зависит от того, где объявлена ​​переменная. Переменные, объявленные в классе, автоматически инициализируются с использованием значения по умолчанию.

object o;
void Method()
{
    if (o == null)
    {
        // this will execute
    }
}

Переменные, объявленные в методе, не инициализируются, но когда переменная впервые используется, компилятор проверяет, что она была инициализирована, поэтому код не будет компилироваться.

void Method()
{
    object o;
    if (o == null) // compile error on this line
    {
    }
}
2 голосов
/ 19 января 2012

В частности, тип ссылки, который не инициализирован, всегда будет иметь нулевое значение

Я думаю, что вы смешиваете локальные переменные и переменные-члены. В разделе 5.3 конкретно говорится о локальных переменных. В отличие от переменных-членов, которые имеют значения по умолчанию, локальные значения по умолчанию никогда не равны нулю или чему-либо еще: они просто должны быть назначены до их первого чтения В разделе 5.3 объясняются правила, которые компилятор использует для определения, назначена ли локальная переменная или нет.

1 голос
/ 19 января 2012

Существует 3 способа присвоения переменной начального значения:

  1. По умолчанию - это происходит (например), если вы объявляете переменную класса без присвоения начального значения, поэтому начальное значение получает default(type), где type - это любой тип, которым вы объявляете переменную.

  2. С инициализатором - это происходит, когда вы объявляете переменную с начальным значением, как в int i = 12;

  3. Любая точка до получения ее значения - это происходит (например), если у вас есть локальная переменная без начального значения. Компилятор гарантирует, что у вас нет доступных путей кода, которые будут считывать значение переменной до ее назначения.

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

0 голосов
/ 19 января 2012

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

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

Опять же, все типы Nullable должны быть проверены на нулевое значение или значение по умолчанию, если они не инициализированы следующим образом:

    int? num = null;
    if (num.HasValue == true)
    {
        System.Console.WriteLine("num = " + num.Value);
    }
    else
    {
        System.Console.WriteLine("num = Null");
    }

    //y is set to zero
    int y = num.GetValueOrDefault();

    // num.Value throws an InvalidOperationException if num.HasValue is false
    try
    {
        y = num.Value;
    }
    catch (System.InvalidOperationException e)
    {
        System.Console.WriteLine(e.Message);
    }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...