9

If you look at next example:

public void TestLocalValuesAssignment()
{
    int valueVariable; // = default(int) suits fine
    string refType; // null suits fine as well

    try
    {
        valueVariable = 5;
        refType = "test";
    }
    catch (Exception){}

    Console.WriteLine("int value is {0}", valueVariable);
    Console.WriteLine("String is {0}", refType);
}

you could easily see, that variables valueVariable and refType could be unassigned before their usage in Console.WriteLine(). Compiler tells us about that with errors:

Error   1   Use of unassigned local variable 'valueVariable'
Error   2   Use of unassigned local variable 'refType'  

This is a widespread case and there are loads of answers on how to fix that (possible fixes commented).

What I can't understand is why such behavior exists? How here local variables are different from class fields, where last ones get default value if not assigned (null for reference types and correspondent default value for value types)? Maybe there's an example or a corner case that explains why such compiler behavior is chosen?

Community
  • 1
  • 1
antonv
  • 1,227
  • 13
  • 21
  • 1
    possible duplicate of [Initialization of instance fields vs. local variables](http://stackoverflow.com/questions/1542824/initialization-of-instance-fields-vs-local-variables) – Tim Schmelter Jan 18 '14 at 21:43
  • 1
    It is not "behavior", it is *rule*. The definite assignment rule states that a variable *must* be assigned before it is used. Clearly it won't be in your code snippet when an exception is raised. Yes, one can happen. – Hans Passant Jan 18 '14 at 21:43
  • @TimSchmelter Totally agree, it's a duplicate. [John Skeet provided](http://stackoverflow.com/a/1542851/472020) rather reasonable explanation that could be considered as an answer to my question. – antonv Jan 18 '14 at 21:53

4 Answers4

14

basically - this is what MS decided.

If you want more you can read here and check Eric Lippert’s Blog

The reason this is illegal in C# is because using an unassigned local has high likelihood of being a bug.

Community
  • 1
  • 1
Mzf
  • 5,210
  • 2
  • 24
  • 37
  • 1
    That link goes to a great Eric Lippert answer. – Ben Voigt Jan 18 '14 at 21:54
  • Agreed, Eric Lippert's answer explains a lot. Basically, it appears to be a rule to prevent programmers from making errors. – antonv Jan 18 '14 at 21:59
  • 2
    @Antonio: Right. Basically, the idea is (1) uninitialized variables are likely to be bugs, and (2) it is very easy to detect when a local is not definitely assigned, but it is quite a bit harder to detect when a field is not definitely assigned. So as a result, the rule is: locals have to be definitely assigned. If it were cheap and easy to find fields that were unassigned, the rule would be extended to fields too, but it isn't cheap and easy. – Eric Lippert Jan 18 '14 at 23:59
  • The blog link is now invalid – Hans Kesting Apr 07 '22 at 11:29
5

It's described in c# spec:

5.1.7 Local variables

A local variable introduced by a local-variable-declaration is not automatically initialized and thus has no default value. For the purpose of definite assignment checking, a local variable introduced by a local-variable-declaration is considered initially unassigned. A local-variable-declaration may include a local-variable-initializer, in which case the variable is considered definitely assigned only after the initializing expression (§5.3.3.4).

Within the scope of a local variable introduced by a local-variable-declaration, it is a compile-time error to refer to that local variable in a textual position that precedes its local-variable-declarator. If the local variable declaration is implicit (§8.5.1), it is also an error to refer to the variable within its local-variable-declarator.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Yes, good refference. It explains the behaviour (or rule) of the compiler clearly. Though it doesn't explains why such decision was made. I was hoping to get some example where it would be obvious why this rule exists. – antonv Jan 18 '14 at 21:51
2

When you do something that appears stupid, like reading from a variable you've never assigned, there are basically two things the compiler can do:

  1. Give you a diagnostic calling your attention to what likely is a mistake.
  2. Do something arbitrary.

Since option #1 helps you find mistakes, it is preferred, especially when the workaround to tell the compiler "No, I mean to use the original default value" is as simple as adding = 0, = null or = default(T).

As for why class members don't work the same way, it's because this can't be checked at compile time (because of the myriad different orders that the different methods could be called). There would be runtime cost of flags whether each member had been assigned, and testing of those flags.

Note that the compiler does enforce the restriction on struct members in a way that's easy to check at compile-time. Namely, each constructor is required to assign every member.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

In reality, your code should be fine, but by strict interpretation, there is a code path which can leave your variables unassigned before use. The try block introduces the potential for code within the block to not be executed (if an exception is thrown), but still execute the code beyond the catch (because there is nothing in the catch such as return or throw to prevent the rest of your method from executing if an exception is thrown in the try).

If you are referring to the difference between initializing "struct" fields and initializing class fields, eg:

public class A
   {
   }

       MyMethod()
       {
           int myInt; // Initialized to zero, yes, but not yet assigned.
                      // An error to use this before assigning it.

           A myA;  // defaults to null, which may be a valid initial state, but still unassigned.
                   // Also an error to use this before assigning it.

           A oneMoreA = null; // Same value as default, but at least intention is clear.
           A anotherA = new A(); // What is or is not happening in the constructor is a separate issue.
                                 // At least anotherA  refers to an actual instance of the class.
Florian
  • 5,918
  • 3
  • 47
  • 86
Zenilogix
  • 1,318
  • 1
  • 15
  • 31