Why compiler throw error CS0165: Use of unassigned local variable?
It's due to the compiler difference.
In this fiddle, https://dotnetfiddle.net/5GgGNS, you can see the error, which is omitted in the mono compiler.
I think the error is valid due to the fact that this line
if (myDict?.TryGetValue("hello", out var value) == true)
is not guaranteed to initialize the local variable value
.
If you would rewrite it to:
if (myDict?.TryGetValue("hello", out var value) == null)
it would try to access value
.
Now, the null
value, or true
in your case, could be a function's return value, which would only be known at run time.
But, since all variables are basically always initialized, it's just a compiler feature.
On the other hand, according to the C#5 specs:
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).
But your code is C# 6.
So my conclusion is that the compilers interpret it differently. The Microsoft compiler takes the ?.
operator into account. You should file it as a bug, or finding at least, maybe even at both parties.
Argumentation
Fun fact, if you use this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.TryGetValue("hello", out var value) == null)
{
Console.WriteLine("Hello" + value.ToString());
}
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
You'll see the actual initialization to default(T)
at work. The output is Hello0
. Nevertheless, it's remarkable because due to the ?
, and the fact that myDict
is null
, TryGetValue
shouldn't be called and leaving value
"uninitialized".
The null-conditional operators are short-circuiting. That is, if one operation in a chain of conditional member or element access operations returns null, the rest of the chain doesn't execute.
source
But..., since there are no uninitialized variables; if it compiles, the compiler will make sure it's behavior is not undefined.
So, since value
is initialized, on run-time, the question remains if it's a valid compiler error at build time. Regarding to the run-time intend of the code it is (and that's why the error was there in the first place), but I think it remains a grey area.
Do note that according to this default(T)
is not override-able, which would actually lead to no condition where it fails.
By running this little test:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
//Your code goes here
Dictionary<string,int> myDict = null;
if (myDict?.Bar(out var test) == null)
{
Console.WriteLine("does hit");
}
}
}
static class Foo
{
public static object Bar(this Dictionary<string,int> input, out int test)
{
test = 3;
Console.WriteLine("does not hit");
return 1;
}
}
[using https://www.jdoodle.com/compile-c-sharp-online , mono 5.10.1]
The output becomes:
does hit
And you can verify the correct run-time behavior of the ?.
operator.
The null conditional ?.
removes the guarantee that value
will be assigned to since TryGetValue
will only be called conditionally if myDict
is not null
.
You enforce the assignment of value
inside the if
statement afterwards with == true
since the left side will return null
if TryGetValue
is not called due to myDict
itself being null. The compiler, however, cannot make this inference leap in the general case so you have to help it out either by testing myDict
for null
beforehand (and skipping ?.
) or initializing value
beforehand.