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.
You are using unassigned variable. Even though the variable is actually assigned, compiler has no way of inferring that from the code you've posted.
All local variables should be initialized when declared anyway, so this is interesting, but still erroneous.
A snippet from the ECMA Standard section 8.3 Variables and Parameters:
A variable shall be assigned before its value can be obtained. The example
results in a compile-time error because it attempts to use the variable a before it is assigned a value. The rules governing definite assignment are defined in §12.3.
Therefore it states that the variable must be assigned before it is used otherwise it results in a compiler error. Because you are creating a delegate and invoking it, the method that is contained within the delegate invocation is technically not known. Therefore the compiler would not be to figure it out. Remember it is the Delegate's Invoke method that is being called not the actual method.
ECMA Standard for C#
This is not a bug.
Section 5.3.3.29 of the C# Language Specification (4.0) outlines the definite assignment rules regarding anonymous functions, including lambda expressions. I will post it here.
You can see how this applies to your specific example. The variable
test
is not specifically assigned prior to the declaration of the lambda expression. It is not specifically assigned prior to the execution of the lambda expression. And it is not specifically assigned after the completion of the lambda expression execution. By rule, the compiler does not consider the variable to be definitely assigned at the point of it being read in theif
statement.As for why, I can only repeat what I have read on the matter, and only what I can remember as I cannot produce a link, but C# does not attempt to do this because, although this is a trivial case that the eye can see, it is far more often the case that this type of analysis would be non-trivial and indeed could amount to solving the halting problem. C# therefore "keeps it simple" and requires you to play by much more readily applicable and solvable rules.
When the compiler is performing control flow analysis of methods to determine whether or not a variable is definitely assigned it will only look within the scope of the current method. Eric Lippert discusses this in this blog post. It's theoretically possible for the compiler to analyze methods called from within the "current method" to reason about when a variable is definitely assigned.
Keep in mind that your code example is not truely a single method. The anonymous method will be refactored into another class, an instance of it will be created, and it will be calling a method that resembles your definition. Additionally the compiler would need to analyze the definition of the
delegate
class as well as the definition ofAction
to reason that the method you provided was actually executed.So while it's within the bounds of theoretical possibility for the compiler to know that the variable is reachable in that context, the compiler writers deliberately choose not to both due to the complexity of writing the compiler for it, and also the (potentially significant) increase in time it would take to compile programs.