tags:

views:

1095

answers:

7

I have always been wondering about why in the following example it is OK to not initialize the instance field (relying that it will have its default value) and accessing it, while local variables apparently must be initialized, even if I initialize it to default value it would get anyway...

  public class TestClass
  {
    private bool a;

    public void Do()
    {
      bool b; // That would solve the problem: = false;
      Console.WriteLine(a);
      Console.WriteLine(b); //Use of unassigned local variable 'b'
    }
  }
+6  A: 

For local variables, the compiler has a good idea of the flow - it can see a "read" of the variable and a "write" of the variable, and prove (in most cases) that the first write will happen before the first read.

This isn't the case with instance variables. Consider a simple property - how do you know if someone will set it before they get it? That makes it basically infeasible to enforce sensible rules - so either you'd have to ensure that all fields were set in the constructor, or allow them to have default values. The C# team chose the latter strategy.

Jon Skeet
Thing is, I like the instance field's behaviour...Hence, it could have been a decision that local variables are "implicitly" initialized to its default value, but the compiler team decided against it based on the assumption that it would reduce bugs due to usage of "uninitialized" variables.
flq
Also initializing variables to default values has a runtime cost, it's difficult to determine if all fields of an object are definitely assigned in the presence of virtual methods so the runtime cost of initializing all fields to default values is payed, in case of locals that cost isn't necessary as it can be determined if a local variable is assigned before it is read, as you said.
Pop Catalin
@Frank: Yes, the compiler team has decided it's a good idea to try to stop people from creating bugs. That seems to be a good decision to me :) If you want to assign default values to all your local variables it's easy enough to do so, but why bother? It's very rare that the rules for definite assignment let you down, and it's easy enough to give a default value in *just those cases*. Safety is a good thing :)
Jon Skeet
I think pretty much all answers make a valid contribution to the question but in order to have this question marked as answered I have chosen this one as being the first one.
flq
A: 

Instance variables have a default value. From the C# 3.0 specification:

5.1.2.1 Instance variables in classes

An instance variable of a class comes into existence when a new instance of that class is created, and ceases to exist when there are no references to that instance and the instance’s finalizer (if any) has executed.

The initial value of an instance variable of a class is the default value (§5.2) of the variable’s type.

For the purpose of definite assignment checking, an instance variable is considered initially assigned.

Razzie
I think the point of the question is asking *why* instance variables have a default, but local variables don't.
Jon Skeet
It's a matter of what the compiler can prove. The compiler can prove that a local variable hasn't been initialized, but a class or instance variable has many paths of access and can't be proven one way or another.
Adam Ruth
A: 

It's a compiler limitation. The compiler tries to prevent you using an unassigned variable wherever it can, which is a good thing as using uninitialised variables used to be a common source of bugs in old C code.

The compiler can't however know whether the instance variable is initialised before you hit that method call because it could be set by any other method, which may be called in any order by external code.

Steven Robbins
it's not a compiler limitation, this is enforced by the runtime, the runtime requires that all locals are initialized and also ensures all fields of new objects are set to default values.
Pop Catalin
I mean the fact it doesn't give an compilation error is a compiler limitation, that's pretty obvious from my answer.
Steven Robbins
Pop: I am unable to find any part of the CLI spec that states that the CLI requires all locals to be initialized. Can you point out to us where that is, if there is in fact such a rule in the CLI?
Eric Lippert
@Eric Lippert, ECMA 335 - 12.4.1 Method Calls
Pop Catalin
+1  A: 

The implicit constructor initializes the instance variable for you. Even when you specify a c'tor but don't initialize a field, it's done for you as part of creating the object on the heap. That's not true of stack local variables.

psychotik
not true, the implicit constructor only exists when no constructor is specifically created, you're confusing terms, the runtime initializes the fields to default values not the implicit constructor (a class may not have a parameter less constructor at all)
Pop Catalin
My first sentence was specifically for the code posted by the OP. My second sentence was more general in cases when a non-default c'tor is provided. I parse my answer and your comment as identical. What am I missing?
psychotik
Like I said you're confusing terms, the implicit parameter less constructor does "nothing" in his case, it's just there as a contract to show that the class ca be instantiated. It's the runtime that initializes the fields to default values not the constructor, that parameterles implicit constructor may as well be missing in the presence of another constructor with parameters.
Pop Catalin
@psychotik, to be more clear, an "implicit constructor" is a parameterless constructor created by the C# compiler when no other constructor is defined in code. It's not always created, only in that particular case, and the class is not required to have a parameterless constructor at all.
Pop Catalin
It's about what the compiler can and can't be 100% certain of. For local variables the compiler can be 100% certain that a variable hasn't be set, but for class and instance variables it can't be (any method could set it, and the compiler doesn't know which of those methods may be called first.) If it could be 100% certain than it would issue the same error for those variables as well.
Adam Ruth
Pop Catalin is correct. That is, your first sentence -- that the implicit default constructor initializes the variable -- is incorrect. Your second sentence -- that it is done for you when the runtime creates an object on the heap -- is correct.
Eric Lippert
+2  A: 

It is governed by Definite Assignment rules in C#. Variable must be definitely assigned before it is accessed.

5.3 Definite assignment

At a given location in the executable code of a function member, a variable is said to be definitely assigned if the compiler can prove, by a particular static flow analysis (§5.3.3), that the variable has been automatically initialized or has been the target of at least one assignment.

5.3.1 Initially assigned variables

The following categories of variables are classified as initially assigned:

• Static variables.

• Instance variables of class instances.

• Instance variables of initially assigned struct variables.

• Array elements.

• Value parameters.

• Reference parameters.

• Variables declared in a catch clause or a foreach statement.

5.3.2 Initially unassigned variables

The following categories of variables are classified as initially unassigned:

• Instance variables of initially unassigned struct variables.

• Output parameters, including the this variable of struct instance constructors.

• Local variables, except those declared in a catch clause or a foreach statement.

Dzmitry Huba
+1  A: 

When a chunk of memory is allocated for a new object instance, the runtime writes zeros across the entire block, ensuring that the new object starts at a known state - this is why integers default to 0, doubles default to 0.0, pointers & object references to null, and so on.

It would be possible, in theory, to do the same to stack frames allocated as as part of method calls. The overhead, though would be high - it would drastically slow down calls to other methods, and therefore isn't attempted.

Bevan
Sorry, I don't quite understand the zeroed-out thing, but this sounds interesting...
flq
+1  A: 

It's really just a matter of what the warning is able to tell you. There's really no way for the warning to be sure that some other method hasn't initialized the class variable, so it only warns on the one it can be certain isn't initialized.

Also, it's a warning and not an error because there's nothing technically wrong with using the unassigned variable (it's guaranteed to be 'false') but it's probably a logic error to have it unassigned.

Adam Ruth
What do you mean it's a warning? In Visual Studio it comes out as an error by default. Also, I was sort of aware that it is a lot easier to figure out locally if a variable is initialized - I was rather wondering why I don't also get default values in the local scenario.
flq
@Frank, because there's a difference between variables initialized to default values and uninitialized variables, you might want the first sometimes, but if by error you happen to read an uninitialized variable you should not get the default value in all cases because that could simply be a programing error not a desired outcome.
Pop Catalin
You're correct, it is an error in .NET (I got confused with other compilers.)From the docs: "The C# compiler does not allow the use of uninitialized variables..." The reason the C# compiler allows non-local variables to be initialized is because it can't tell whether or not they have been initialized. Only local variables can be PROVEN, by the compiler, to be not initialized.
Adam Ruth
Adam: though I take your point, you're actually saying it backwards. The compiler never attempts to prove that a local variable *hasn't* been initialized. Rather, it *fails* to prove that a local variable *has* been initialized. Proving the negative gives us no useful information! It's the inability to prove the positive that gives us something we can act upon.
Eric Lippert
An example: "int x, y = 0; if (0 == y*0) x = 10; print(x);" We give a definite assignment error here in C# 3. We do not prove that x is always unassigned -- if we did, then our proof system would be incorrect, because clearly x always is assigned. Rather, because the compiler is not sophisticated enough to prove that x is always assigned, we give an error. It is our *failure* to prove the *positive* that is the error, not our *success* in proving the *negative*.
Eric Lippert