I have some code similar to the following.
class MyClass<TEnum> where TEnum : struct
{
public IEnumerable<TEnum> Roles { get; protected set; }
public MyClass()
{
IEnumerable<string> roles = ... ;
TEnum value;
Roles = from r in roles
where Enum.TryParse(r, out value)
select value; // <---- ERROR HERE!
}
}
However, on the line indicated above, I get the error:
Use of unassigned local variable 'value'
Seems to me that value
will always be initialized in this case since it is an out
parameter to Enum.TryParse
.
Is this a bug with the C# compiler?
TL;DR: Error says the variable is (provably) unassigned -- FALSE. Reality, variable is not provably assigned (using the proof theorems available to the compiler).
LINQ is designed with the assumption of pure functions... those that return outputs based on the input and have no side effects.
Once this is rewritten, it will be:
roles.Where(r => Enum.TryParse(r, out value)).Select(r => value);
and rewritten again to
Enumerable.Select(Enumerable.Where(roles, r => Enum.TryParse(r, out value)), r => value);
These LINQ functions will call the filter lambda before any calls to the selection lambda, but the compiler can't know that (at least, not without either special-casing or cross-module dataflow analysis). More problematically, if a different implementation of Where
were chosen by overload resolution, then possibly the lambda with TryParse
would NOT be called.
The compiler's rules for definite assignment are very simplistic, and err on the side of safety.
Here is another example:
bool flag = Blah();
int value;
if (flag) value = 5;
return flag? value: -1;
It is impossible for an uninitialized value to be used, yet the language rules for dataflow analysis lead to a compile error that value
is used without being "definitely assigned".
The compiler error is worded poorly, however. Not being "definitely assigned" is not the same as definitely being "unassigned", as the error purports.