Consider the following source:
static void Main(string[] args)
{
bool test;
Action lambda = () => { test = true; };
lambda();
if (test)
Console.WriteLine("Ok.");
}
It should compile, right? Well, it doesn't. My question is: according to C# standard, should this code compile or is this a compiler bug?
The error message:
Use of unassigned local variable 'test'
Note: I know, how to fix the error and i partially know, why does it happen. However, the local variable is assigned unconditionally and I guess, that compiler should notice that, but it does not. I wonder, why.
Comment for answers: C# allows declaring unassigned variables and that's actually quite useful, ie.
bool cond1, cond2;
if (someConditions)
{
cond1 = someOtherConditions1;
cond2 = someOtherConditions2;
}
else
{
cond1 = someOtherConditions3;
cond2 = someOtherConditions4;
}
Compiler compiles this code properly and I think, that leaving variables unassigned actually makes the code a little bit better, because:
- It tells the reader, that values are assigned later (mostly probably in the following conditional statement)
- Forces the programmer to assign the variables in all branches of internal conditions (if it was the purpose of this code from the beginning), because compiler will refuse to compile the code if one of the branches does not assign one of them.
On the margin: That's even more interesting. Consider the same example in C++:
int main(int argc, char * argv[])
{
bool test;
/* Comment or un-comment this block
auto lambda = [&]() { test = true; };
lambda();
*/
if (test)
printf("Ok.");
return 0;
}
If you comment the block out, compilation ends with warning:
main.cpp(12): warning C4700: uninitialized local variable 'test' used
However, if you remove the comment, compiler emits no warnings whatsoever. It seems to me, that it is able to determine, if the variable is set after all.
testhas been reached. I don't know if that is because of basic code flow analysis that doesn't follow method calls, or if it is due to the way closed over locals are turned into code-genned class members, or perhaps both or neither. But either way, initialize the variable to false upon declaration.testassignment is reached, when there are no conditional expressions on the way?delegate { test = true; };). Second, knowing that the local variable is assigned within the delegate is not hard. In fact, to maintain closure semantics is must already know that the local is accessed in the closure and hoist it. The difficult question is determining whether or not that delegate is executed at some point and marking the variable as having a defined value at that point. It's a non-trivial problem in the general case.