Неинициализированные против нулевых значений ссылочных типов - PullRequest
7 голосов
/ 22 января 2011

Есть ли разница между переменной ссылочного типа, которая не инициализирована или имеет нулевое значение?Я где-то читал, что не-init означает ноль, но в другом месте я читаю что-то еще.Спасибо!

Ответы [ 2 ]

15 голосов
/ 22 января 2011

Обратите внимание, что поля неявно инициализируются в null, так что это влияет только на переменные. В чистом c # вы не можете запросить значение неинициализированного поля (вам нужно «определенное присваивание»), так что это не вопрос.

Вы можете сделать это, злоупотребив IL, хотя - объявив параметр out и используя DynamicMethod, чтобы написать метод, который не назначает его (допустимый в IL, но не в C # ). И тогда вы обнаружите, что вы увидите null s.

Это, в свою очередь, происходит из-за флага IL (.locals init), который говорит: «очистите для меня стек перед входом в этот метод» в , вызывающем код (C #). Компилятор C # всегда устанавливает этот флаг. Если вы снова злоупотребляете IL, чтобы написать метод, который не устанавливает этот флаг, вы можете увидеть мусор. Это может быть что угодно . Но к этому моменту вы заслуживаете исключений, которые вы получаете:)

Вот пример первого (не второго, который является более сложным):

delegate void AbuseMe(out object foo);
static void Main() {
    DynamicMethod dyn = new DynamicMethod("Foo",
        typeof(void), new[] { typeof(object).MakeByRefType() });
    dyn.GetILGenerator().Emit(OpCodes.Ret);
    AbuseMe method = (AbuseMe) dyn.CreateDelegate(typeof(AbuseMe));
    object obj; // this **never** gets assigned, by **any** code
    method(out obj);
    Console.WriteLine(obj == null);
}

Для пояснения, код DynamicMethod просто пишет эквивалент этого кода, недопустимый в C #:

static void Foo(out object whatever) { } // note, whatever is not assigned

Это работает, потому что в отношении CLR out не существует - существует только ref. Так что это не недействительный IL - только язык (C #) придает значение out и требует, чтобы ему было присвоено значение.

Проблема в том, что Main() все еще имеет флаг .locals init; поэтому за кадром obj очищается до null (ну, все пространство стека просто стирается). Если бы я компилировал из IL без этого флага (и имел какой-то другой код, чтобы сделать пространство стека грязным), я мог бы видеть мусор. Вы можете увидеть больше о .locals init в блоге Лиран Чен .

Но чтобы ответить на вопрос:

  • для полей: поля неинициализированного ссылочного типа null - гарантируется спецификацией
  • для переменных: вы не можете спрашивать , но как подробности реализации (от этого не следует зависеть): да, это будет null даже хотя вы не можете спросить; p
3 голосов
/ 22 января 2011

"Это зависит"

Для обычных переменных-членов , когда значение не указано в объявлении, тогда переменная принимает соответствующее значение по умолчанию (null для ссылочных типов).То есть class A { string X; } - это то же самое, что и class A { string X = null; }.

Для локальных переменных доступ к ним является ошибкой до того, как может быть подтверждено присвоение значения.Даже если их тип "по умолчанию" равен нулю (для ссылочных типов), они не назначаются неявно по умолчанию!То есть string F () { string x; return x; } - это ошибка времени компиляции.

Помните: null - это null: -)

...