Clarification of question: I am not looking for answers on how to solve this issue (several are listed below), but as to why it is happening.
I expect the following code to compile:
struct Alice
{
public string Alpha;
public string Beta;
}
struct Bob
{
public long Gamma;
}
static object Foo(dynamic alice)
{
decimal alpha;
long beta;
if (!decimal.TryParse(alice.Alpha, out alpha) // *
|| !long.TryParse(alice.Beta, out beta)) // **
{
return alice;
}
var bob = new Bob { Gamma = beta }; // ***
// do some stuff with alice and bob
return alice;
}
However the following compile time error is thrown at // ***
:
Use of unassigned local variable 'beta'
I can make the program compile under the following situations:
If I change the signature to be
static object Foo(Alice alice)
Explicitly casting on the lines // *
and // **
, e.g.:
!long.TryParse((string)alice.Beta, out beta)
.
Removing the decimal.TryParse
on line // *
.
Replacing the short circuit or ||
with |
. Thanks to HansPassant
Swapping the TryParse
s around
Pulling the results of the TryParse
s into bool
s Thanks to Chris
Assigning a default value to beta
Am I missing something obvious, or is there something subtle going on, or is this a bug?
Mismatched brackets If the brackets don't all match up, the result is a compile time error. The fix to this compile error is to add a leading round bracket after the println to make the error go away: int x = 10; System.
A compile time error is an error that is detected by the compiler. Common causes for compile time errors include: Syntax errors such as missing semi-colon or use of a reserved keyword (such as 'class'). When you try and access a variable that is not in scope.
The Errors that occur due to incorrect syntax are known as compile-time errors or sometimes also referred to as syntax errors in java. Examples of compile-time errors include: missing parenthesis, missing a semicolon, utilizing undeclared variables, etc.
The compile-time errors can be: Syntax errors. Semantic errors.
I don't know answer for sure, but for me it looks like compiler bug or "by design" issue.
I played with your sample a bit, reducing it bit by bit, and here is what left from it:
private static bool Default<T>(out T result)
{
result = default(T);
return true;
}
private static void Foo()
{
int result;
if (true || Default(out result))
{
return;
}
Console.WriteLine(result);
}
Which also fails with
error CS0165: Use of unassigned local variable 'result'
You can play with int result
in Foo
to check any type you want.
Please notice there's no dynamic
usage, and also please notice true
branch which should immediately return.
So for me it looks like VS.Net compiler is "not intelligent enough" here.
What is good with this piece of code - it can be compiled with compilers prior to .Net 4 (using csc.exe
from appropriate frameworks), so here are the results:
Build ok, warnings:
warning CS0429: Unreachable expression code detected
warning CS0162: Unreachable code detected
Build failed:
error CS0165: Use of unassigned local variable 'result'
So if it is a bug, it appears somewhere in between .NET 2 and .NET 3.5
It happens because the dynamic
keyword causes a lot of changes in the generated code structure (generated by the C# compiler).
You can observe this using a tool such as .NET reflector (I suggest to select 'None' for C# inference so you can really see all the generated stuff). Basically, each time you access a dynamic
object, the generated code adds at least an if
case. These ifs
can cause important code paths changes.
For example, this simple C# code
static void MyFoo(dynamic dyn)
{
if (dyn == null)
return;
var x = dyn;
}
is generated as:
private static void MyFoo([Dynamic] object dyn)
{
object obj2;
CSharpArgumentInfo[] infoArray;
bool flag;
if (<MyFoo>o__SiteContainer0.<>p__Site1 != null)
{
goto Label_0038;
}
<MyFoo>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, bool>>.Create(Binder.UnaryOperation(0, 0x53, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) }));
Label_0038:
if (<MyFoo>o__SiteContainer0.<>p__Site2 != null)
{
goto Label_0088;
}
<MyFoo>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, object, object>>.Create(Binder.BinaryOperation(0, 13, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null), CSharpArgumentInfo.Create(2, null) }));
Label_0088:
if ((<MyFoo>o__SiteContainer0.<>p__Site1.Target(<MyFoo>o__SiteContainer0.<>p__Site1, <MyFoo>o__SiteContainer0.<>p__Site2.Target(<MyFoo>o__SiteContainer0.<>p__Site2, dyn, null)) == 0) == null)
{
goto Label_00AE;
}
obj2 = dyn;
Label_00AE:
return;
}
Let's take a another simple example. This code:
static void MyFoo1(dynamic dyn)
{
long value;
if (long.TryParse(dyn, out value))
return;
var x = value;
}
compiles fine. This one
static void MyFoo2(dynamic dyn)
{
long value;
if (true || long.TryParse(dyn, out value))
return;
var x = value;
}
doesn't. If you look at the generated code (set value to 0 to make sure it compiles), it will simply contain some extra ifs
and goto
that change the whole code path quite drastically. I don't think it's a bug, maybe more a limitation of the dynamic
keyword (that has quite a few, for example: Limitations of the dynamic type in C#)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With