7

Is there a difference between reference type variable being non initialized or having null value? I read somewhere that non-init means null but on other place I read something else. Thanks!

Lojol
  • 645
  • 3
  • 8
  • 15

2 Answers2

16

Note that fields are implicitly initialized to null, so this only affects variables. In pure c# you can't query the value of an uninitialized field (you need "definite assignment"), so it is a non-question.

You can do this by abusing IL though - by declaring an out parameter, and using DynamicMethod to write a method that doesn't assign it (valid in IL, but not in C#). And then you find that you will see nulls.

This in turn is due to an IL flag (.locals init) that says "clear the stack for me before entering this method" on the calling (C#) code. The C# compiler always sets this flag. If you again abuse IL to write a method that doesn't set this flag, you can probably see garbage. It could be anything. But by this point, you deserve the exceptions you get :)

Here's an example of the first (not the second, which is more complex):

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);
}

For clarification, the DynamicMethod code is simply writing the equivalent of this code, not legal in C#:

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

This works because as far as the CLR is concerned out doesn't exist - there is only ref. So this isn't invalid IL - it is only the language (C#) that puts meaning to out and demands that it be assigned a value.

The problem is that Main() still has the .locals init flag; so behind the scenes obj is cleared to null (well, the entire stack space is simply wiped). If I compiled from IL without that flag (and had some other code in place to make the stack space dirty) I could see garbage. You can see more about .locals init on Liran Chen's blog.

But to answer the question:

  • for fields: uninitialized reference-type fields are null - guaranteed by the spec
  • for variables: you can't ask, but as an implementation detail (that should not be depended on): yes, it will be null even though you can't ask ;p
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    Ok, that is pretty freaky sh!t! :) – leppie Jan 22 '11 at 21:09
  • Jeffrey Richter in his book "CLR Via C#, 3rd edition" mentions that variables are always initialized by CLR to `null`. He also says that actually it's not necessary for C# compiler to forbid using non-initialized variables, but the compiler still has this restriction. – Dmitrii Lobanov Jan 22 '11 at 21:13
  • @Dmitry exactly, which is why I stressed "should not be depended on". This behaviour is not defined by the C# spec (it doesn't need to be : the entire scenario is already precluded), so any implementation is valid. "Set the locals to monotonically increasing bytes" would be a perfectly legal implementation ;p – Marc Gravell Jan 22 '11 at 21:16
  • @Dmitry and that CLR "rule" is, unless I am mistaken, driven by the `.locals init` flag. IIRC you can omit this, and let the chaos commence. – Marc Gravell Jan 22 '11 at 21:17
4

"It depends"

For normal member variables when a value is not specified in the declaration then the variable assume the appropriate default value (null for reference types). That is, class A { string X; } is the same as class A { string X = null; }.

For local variables it is an error to access them before a value can be proven to have been assigned. Even though their type "defaults" to null (for reference types), they are not default implicitly assigned! That is, string F () { string x; return x; } is a compile-time error.

Remember: null is null :-)

Jackson Pope
  • 14,520
  • 6
  • 56
  • 80