为什么对方法的第二个参数使用await运算符会影响第一个参数的值?

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上运行的控制台应用程序.

Pau*_*ado 5

我已经使用C#5编译器和分别使用LinqPad 4LinqPad 5的C#6编译器测试了代码,我可以重现这个问题.

这看起来像是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);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,对于C#5,myValue变量在声明之前声明,foo并且在第一次调用之前从未初始化PrintTwoValues.