2010-03-25 3 views
16

У меня есть следующий фрагмент кода:Non инициализируется переменная в C#

class Foo 
{ 

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

     } 
    } 
} 

class Bar { } 

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

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

+0

В этот момент Bar указывает на место в стеке, а не на кучу. По этой причине ему нужно значение –

+0

И классы инициализируются на куче справа? А структур нет. Верный? – Snake

+0

Но код выполняется в контексте конструктора –

ответ

32

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

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

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

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

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

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

+6

Он управляется флагом * localsinit * в метаданных метода. Подробнее см. Раздел 3.47 раздела «localloc». Кажется, я помню, что CLR всегда инициализирует стек стека независимо от нуля. Или, возможно, всегда это делается при подключении отладчика? Я не уверен. В любом случае, местные жители, наблюдаемые в своем неинициализированном состоянии через подлые средства, почти всегда, похоже, имеют свои значения по умолчанию. –

2

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

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

    } 
} 
7

поля (переменные на классы/структуры) инициализируются null/ноль/и т.д.. Локальные переменные ... хорошо - поскольку (по «определенному назначению») вы не можете получить к ним доступ без назначения, нет разумного способа ответа; просто это не определено, поскольку это невозможно. Я считаю, что произошли, чтобы быть 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); 
    } 
} 

Ил просто делает «РЭТ» - это никогда ничего не назначает.

+0

Я не заметил, что вы нашли способ определения значения, используя параметр out, но я просто добавил это к моему ответу. Зло, гадкие вещи. –

+0

Я не знаю, является ли это глупым вопросом, но почему поведение отличается от полей по сравнению с локальными переменными? Это связано с областью видимости, то есть локальная переменная действительна только в пределах функции? Или есть еще одна причина? –

+1

@ Jon - вам это даже не нужно; если у вас есть тип делегата, который «выдает значение YourType», просто создайте «DynamicMethod» с IL = «ret», вызовите «CreateDelegate» и вызовите делегата «out myVariable». Затем проверьте переменную. Не нужно выполнять тяжелую работу в динамическом методе. –

1

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

1

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

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

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

1

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

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

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

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

0

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

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

0

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

Смежные вопросы