Reputation: 222
The following C# program produces unexpected output. I would expect to see:
Value1: 25, Value2: 10
Value1: 10, Value2: 25
but instead I see
Value1: 0, Value2: 10
Value1: 10, Value2: 25
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
DoWork().Wait();
Console.ReadLine();
}
private async static Task DoWork()
{
SomeClass foo = new SomeClass()
{
MyValue = 25.0f
};
PrintTwoValues(foo.MyValue, await GetValue());
PrintTwoValues(await GetValue(), foo.MyValue);
}
static void PrintTwoValues(float value1, float value2)
{
Console.WriteLine("Value1: {0}, Value2: {1}", value1, value2);
}
static Task<float> GetValue()
{
return Task.Factory.StartNew(() =>
{
return 10.0f;
});
}
class SomeClass
{
private float myValue;
public float MyValue
{
get
{
return this.myValue;
}
set
{
this.myValue = value;
}
}
}
}
}
Can somebody explain to me why it is that using the "await" operator in the expression for the second argument to the PrintTwoValues
method seems to be affecting the value of the first argument?
My guess is that it must have something to do with the fact that the argument list is evaluated left-to-right. In the first call to PrintTwoValues
I'm guessing that the return value from SomeClass.MyValue
gets pushed onto the stack. Then execution continues into GetValue
which just starts the Task and exits. Then DoWork
exits and schedules a continuation that will call PrintTwoValues
but when that continuation runs the value that had originally gotten pushed on the stack is somehow lost and reverted back to its default value.
While there are simple ways to workaround this problem, like storing the arguments in temporary variables before passing them to the PrintTwoValues
method, I'm mostly just curious why this behavior is occurring.
Note: I'm using Visual Studio 2013, Update 5. I'm building a console application that is targeting .NET Framework 4.5 and running on Windows 10 Enterprise.
Upvotes: 8
Views: 147
Reputation: 14856
I've tested the code both with the C#5 compiler and the C#6 compiler using respectively LinqPad 4 and LinqPad 5 and I could reproduce the issue.
This looks like a compiler bug of the C#5 compiler, because when I decomplied both versions with .NET Reflector 9, I got different code:
private async static Task DoWork()
{
float myValue;
SomeClass foo = new SomeClass {
MyValue = 25f
};
float introduced6 = await GetValue();
PrintTwoValues(myValue, introduced6);
float introduced7 = await GetValue();
PrintTwoValues(introduced7, foo.MyValue);
}
private async static Task DoWork()
{
SomeClass foo = new SomeClass {
MyValue = 25f
};
float myValue = foo.MyValue;
float num2 = await GetValue();
float asyncVariable1 = num2;
PrintTwoValues(myValue, asyncVariable1);
num2 = await GetValue();
float asyncVariable2 = num2;
PrintTwoValues(asyncVariable2, foo.MyValue);
}
Notice that, for C#5, the myValue
variable is declared before the declaration of of foo
and never initialized before the first call to PrintTwoValues
.
Upvotes: 5