为什么对方法的第二个参数使用 await 运算符会影响第一个参数的值?
Why does using the await operator for the second argument to a method affect the value of the first argument?
以下 C# 程序产生意外输出。我希望看到:
Value1: 25, Value2: 10
Value1: 10, Value2: 25
但我看到了
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;
}
}
}
}
}
有人可以向我解释为什么在 PrintTwoValues
方法的第二个参数的表达式中使用“await”运算符似乎会影响第一个参数的值吗?
我的猜测是,这一定与参数列表是从左到右计算的事实有关。在第一次调用 PrintTwoValues
时,我猜测来自 SomeClass.MyValue
的 return 值被压入堆栈。然后继续执行到 GetValue
,它只是启动任务并退出。然后 DoWork
退出并安排一个将调用 PrintTwoValues
的延续,但是当该延续运行时,最初压入堆栈的值不知何故丢失并恢复为默认值。
虽然有一些简单的方法可以解决这个问题,比如在将参数传递给 PrintTwoValues
方法之前将参数存储在临时变量中,但我主要只是好奇为什么会出现这种情况。
注意:我正在使用 Visual Studio 2013 更新 5。我正在构建一个控制台应用程序,该应用程序的目标是 .NET Framework 4.5 和 运行 on Windows 10 Enterprise。
我已经分别使用 C#5 编译器和 C#6 编译器测试了代码 LinqPad 4 and LinqPad 5,我可以重现该问题。
这看起来像是 C#5 编译器的编译器错误,因为当我用 .NET Reflector 9 反编译两个版本时,我得到了不同的代码:
C#5:
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);
}
C#6:
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);
}
请注意,对于 C#5,myValue
变量在 foo
的声明之前声明,并且在第一次调用 PrintTwoValues
.[=17= 之前从未初始化]
以下 C# 程序产生意外输出。我希望看到:
Value1: 25, Value2: 10
Value1: 10, Value2: 25
但我看到了
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;
}
}
}
}
}
有人可以向我解释为什么在 PrintTwoValues
方法的第二个参数的表达式中使用“await”运算符似乎会影响第一个参数的值吗?
我的猜测是,这一定与参数列表是从左到右计算的事实有关。在第一次调用 PrintTwoValues
时,我猜测来自 SomeClass.MyValue
的 return 值被压入堆栈。然后继续执行到 GetValue
,它只是启动任务并退出。然后 DoWork
退出并安排一个将调用 PrintTwoValues
的延续,但是当该延续运行时,最初压入堆栈的值不知何故丢失并恢复为默认值。
虽然有一些简单的方法可以解决这个问题,比如在将参数传递给 PrintTwoValues
方法之前将参数存储在临时变量中,但我主要只是好奇为什么会出现这种情况。
注意:我正在使用 Visual Studio 2013 更新 5。我正在构建一个控制台应用程序,该应用程序的目标是 .NET Framework 4.5 和 运行 on Windows 10 Enterprise。
我已经分别使用 C#5 编译器和 C#6 编译器测试了代码 LinqPad 4 and LinqPad 5,我可以重现该问题。
这看起来像是 C#5 编译器的编译器错误,因为当我用 .NET Reflector 9 反编译两个版本时,我得到了不同的代码:
C#5:
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);
}
C#6:
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);
}
请注意,对于 C#5,myValue
变量在 foo
的声明之前声明,并且在第一次调用 PrintTwoValues
.[=17= 之前从未初始化]