Неинициализированная переменная в C # - PullRequest
17 голосов
/ 25 марта 2010

У меня есть следующий фрагмент кода:

class Foo
{

    public Foo()
    {
        Bar bar;
        if (null == bar)
        {

        }
    }
}

class Bar { }

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

Так что теперь я задаюсь вопросом: что такое значение бара, не должно ли оно быть нулевым? Разве они не установлены в ноль? (нулевой указатель?)

Ответы [ 8 ]

35 голосов
/ 25 марта 2010

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

См. Раздел 5.3 спецификации C # 3.0 для более подробной информации о конкретном назначении.

Обратите внимание, что это не имеет никакого отношения к тому, что это переменная ссылочного типа. Это не удастся скомпилировать таким же образом:

int i;
if (i == 0) // Nope, i isn't definitely assigned
{
}

1 Что касается языка, то в любом случае ... ясно, что место хранения в памяти содержит что-то , но оно не имеет значения и зависит от реализации. Существует один способ узнать, что это за значение, создав метод с параметром out, но затем используя IL для просмотра значения этого параметра в методе, не задав ему другого значение. CLR не возражает против этого вообще. Затем вы можете вызвать этот метод, передав не определенную переменную, и вот, вы можете обнаружить значение - которое, скорее всего, будет значением "все нули".

Я подозреваю, что спецификация CLI обеспечивает принудительное применение локальных переменных, имеющих значение по умолчанию - но я должен проверить. Если вы не делаете злых дел, как указано выше, это не должно иметь значения для вас в C #.

8 голосов
/ 25 марта 2010

Поля (переменные для классов / структур) инициализируются как null / ноль / и т. Д. Локальные переменные ... хорошо - поскольку (с помощью "определенного присваивания") вы не можете получить к ним доступ без присваивания, то нет никакого разумного способа ответа; просто это не определено, так как это невозможно. Я полагаю, что они случаются как null / ноль / и т. Д. (Доказуемые путем взлома некоторого out кода с помощью динамической генерации IL), но это деталь реализации.


Для информации, вот некоторый хитрый код, который показывает значение формально неинициализированной переменной:

using System;
using System.Reflection.Emit;
static class Program
{
    delegate void Evil<T>(out T value);
    static void Main()
    {
        MakeTheStackFilthy();
        Test();
    }
    static void Test()
    {
        int i;
        DynamicMethod mthd = new DynamicMethod("Evil", null, new Type[] { typeof(int).MakeByRefType()});
        mthd.GetILGenerator().Emit(OpCodes.Ret); // just return; no assignments
        Evil<int> evil = (Evil<int>)mthd.CreateDelegate(typeof(Evil<int>));
        evil(out i);
        Console.WriteLine(i);
    }
    static void MakeTheStackFilthy()
    {
        DateTime foo = new DateTime();
        Bar(ref foo);
        Console.WriteLine(foo);
    }
    static void Bar(ref DateTime foo)
    {
        foo = foo.AddDays(1);
    }
}

IL просто делает "ret" - он никогда ничего не назначает.

2 голосов
/ 25 марта 2010

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

public Foo()
{
    Bar bar = null;
    if (null == bar)
    {

    }
}
1 голос
/ 25 марта 2010

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

(Однако локальная переменная может быть оптимизирована для использования регистра вместо пространства стека, но она все еще не определена.)

Компилятор не позволит вам использовать неопределенное значение, он должен иметь возможность определить, что переменная инициализирована, прежде чем вы сможете ее использовать.

Для сравнения, VB инициализирует локальные переменные. Хотя иногда это может быть практичным, это может также означать, что вы непреднамеренно используете переменную до того, как вы дадите ей значащее значение, и компилятор не может определить, делать это или нет.

1 голос
/ 25 марта 2010

Нет, локальные переменные не устанавливаются автоматически в 0 (по умолчанию).

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

Не путать с переменными поля (членами класса), они инициализируются значением по умолчанию их типа (0 / null / false / ...).

1 голос
/ 25 марта 2010

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

0 голосов
/ 01 августа 2010

Помимо «правильности», инициализация локальной переменной также связана с процессом проверки CLR .
Для получения дополнительной информации см. Мой ответ на этот похожий вопрос: Почему локальные переменные должны иметь начальные значения

0 голосов
/ 25 марта 2010

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

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

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