Подтвердить что ты не робот

Не инициализированная переменная в С#

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

class Foo
{

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

        }
    }
}

class Bar { }

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

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

4b9b3361

Ответ 1

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

Подробнее см. раздел 5.3 спецификации С# 3.0.

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

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

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

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

Ответ 2

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


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

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" - он никогда ничего не присваивает.

Ответ 3

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

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

    }
}

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

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