sup*_*war 8 c# parameters arguments parameter-passing async-await
以下C#程序产生意外输出.我希望看到:
值1:25,值2:10
值1:10,值2:25
但相反,我明白了
值1:0,值2:10
值1:10,值2: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;
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释为什么在PrintTwoValues方法的第二个参数的表达式中使用"await"运算符似乎会影响第一个参数的值吗?
我的猜测是它必须与从左到右评估参数列表的事实有关.在第一次调用PrintTwoValues我猜测返回值SomeClass.MyValue被推入堆栈.然后继续执行GetValue,然后启动任务并退出.然后DoWork退出并调度将调用的延续,PrintTwoValues但是当该延续运行时,最初在堆栈上推送的值会以某种方式丢失并恢复为其默认值.
虽然有简单的方法可以解决这个问题,比如在将参数传递给PrintTwoValues方法之前将参数存储在临时变量中,但我只是好奇为什么会出现这种情况.
注意:我正在使用Visual Studio 2013,Update 5.我正在构建一个针对.NET Framework 4.5并在Windows 10 Enterprise上运行的控制台应用程序.
我已经使用C#5编译器和分别使用LinqPad 4和LinqPad 5的C#6编译器测试了代码,我可以重现这个问题.
这看起来像是C#5编译器的编译器错误,因为当我使用.NET Reflector 9解压缩这两个版本时,我得到了不同的代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,对于C#5,myValue变量在声明之前声明,foo并且在第一次调用之前从未初始化PrintTwoValues.