13

Just curious, I'm not trying to solve any problem.

Why only local variables should be assigned?

In the following example:

class Program
{
    static int a;
    static int b { get; set; }
    static void Main(string[] args)
    {
        int c;
        System.Console.WriteLine(a);
        System.Console.WriteLine(b);
        System.Console.WriteLine(c);
    }
}

Why a and b gives me just a warning and c gives me an error?

Addionally, why I can't just use the default value of Value Type and write the following code?

        bool MyCondition = true;
        int c;
        if (MyCondition)
            c = 10;

Does it have anything to do with memory management?

Androiderson
  • 16,865
  • 6
  • 62
  • 72

3 Answers3

27

Tim gives a good answer to your first question but I can add a few more details.

Your first question is basically "locals are required to be definitely assigned, so why not make the same restriction on fields?" Tim points out that Jon points out that it is actually quite difficult to do so. With locals it is almost always crystal clear when a local is first read and when it is first written. In the cases where it is not clear, the compiler can make reasonable conservative guesses. But with fields, to know when a first read and a first write happens, you have to know all kinds of things about which methods are called in what order.

In short, analyzing locals requires local analysis; the analysis doesn't have to go past the block that contains the declaration. Field analysis requires global analysis. That's a lot of work; it's easier to just say that fields are initialized to their default values.

(Now, that said, it is of course possible to do this global analysis; my new job will likely involve doing precisely this sort of analysis.)

Your second question is basically "Well then, if automatic assignment of default values is good enough for fields then why isn't it good enough for locals?" and the answer is "because failing to assign a local variable and accidentally getting the default value is a common source of bugs." C# was carefully designed to discourage programming practices that lead to irritating bugs, and this is one of them.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
21

Because other variables are initialized with their default value.

Jon Skeet has already found some interesting words on this issue:

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.

and here's the related C# language specification:

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.

Community
  • 1
  • 1
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 1
    Another thing to consider: as far as I know, the specification guarantees that on creation of an instance, the memory space containing that instance is bitwise zeroed. This, in turn, assigns the default values for the member variables - a char variable full of 0s is a \0, an int variable full of 0s is a 0, a float 0.0 and any reference is null etc. โ€“ canahari Jan 19 '13 at 22:50
9

The CLR provides a hard guarantee that local variables are initialized to their default value. But this guarantee does have limitations. What is missing is its ability to recognize scope blocks inside the method body. They disappear once the compiler translates the code to IL. Scope is a language construct that doesn't have a parallel in the CLI and cannot be expressed in IL.

You can see this going wrong in a language like VB.NET for example. This contrived example shows the behavior:

Module Module1
    Sub Main()
        For ix = 1 To 3
            Dim s As String
            If ix = 2 Then s = "foo"
            If s Is Nothing Then Console.WriteLine("null") Else Console.WriteLine(s)
        Next
        Console.ReadLine()
    End Sub
End Module

Output:

null
foo
foo

Or in other words, the local variable s was initialized only once and retains its value afterwards. This has a knack for creating bugs of course. The VB.NET compiler does generate a warning for it and has simple syntax to avoid it (As New). A managed language like C++/CLI has the same behavior but doesn't emit a diagnostic at all. But the C# language gives a stronger guarantee, it generates an error.

This rule is called "definite assignment". The exact rules are explained in detail in the C# Language Specification, chapter 5.3.3

Definite assignment checking has its limitations. It works well for local variables since their scope is very limited (private to the method body) and you can't get to them with Reflection. Much harder to do with fields of a class, it requires whole-program analysis that may need to reach beyond what the compiler can see. Like code in another assembly. Which is why the C# compiler can only warn about it but can't reject the code out-right.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536