Могу ли я указать, какие переменные я хочу сохранить после завершения ожидания?

В рамках метода async любые локальные переменные сохраняются, поэтому, когда любой поток продолжается после того, как await будет иметь доступ к значениям. Есть ли способ указать, какие значения действительно необходимы после await?

Например:

var firstName = "Karl";
var lastName = "Anderson";
var street1 = "123 Nowhere Street";
var street2 = "Apt 1-A";
var city = "Beverly Hills";
var state = "California";
var zip = "90210";

await MyTaskHere();

Console.WriteLine(firstName);
Console.WriteLine(city);

Итак, я объявил 7 локальных переменных, но использую только 2 из них после await, есть ли какой-либо атрибут, который я могу украсить мои переменные, чтобы указать, что я намерен использовать только firstName и city после await завершается?

Примечание. Это надуманный пример, но, похоже, было бы полезно удержать хранение потенциально больших блоков данных, если они не понадобятся, когда следующий поток завершит работу.

Ответ 1

Нет, вы не можете. (За исключением очевидных решений разбиения их на отдельные методы или установки их на null).

В этом сценарии компилятор не полностью оптимизирован; он может захватывать больше переменных, чем нужно, и может удерживать их дольше, чем необходимо. Вероятно, это будет то, что Microsoft будет оптимизировать в будущем.

Ответ 2

Вы можете запустить Ildasm.exe в своей программе, чтобы узнать, какой код генерирует компилятор. Я пытался это сделать, но, к сожалению, мои навыки IL немного не хватает, однако вы можете видеть, что все локальные переменные захватываются как поля сгенерированного класса <Foo>d__0. Учитывая эту программу:

using System;
using System.Threading.Tasks;

namespace AsyncCaptureVariables
{
    class Program
    {
        public async Task Foo()
        {
            var firstName = "Karl";
            var lastName = "Anderson";
            var street1 = "123 Nowhere Street";
            var street2 = "Apt 1-A";
            var city = "Beverly Hills";
            var state = "California";
            var zip = "90210";

            await Task.Delay(5000);

            Console.WriteLine(firstName);
            Console.WriteLine(city);
        }

        public static void Main()
        {
            var program = new Program();
            Task t = program.Foo();
            t.Wait();
        }
    }
}

Компилятор генерирует нечто вроде следующего, частично преобразованного в код С#:

using System;

class Program : System.Object
{
class <Foo>d__0 : System.ValueType, System.Runtime.CompilerServices.IAsyncStateMachine
  {   
    public int32 <>1__state;
    public System.Runtime.CompilerServices.AsyncTaskMethodBuilder <>t__builder;
    public class AsyncCaptureVariables.Program <>4__this;
    public string <firstName>5__1;
    public string <lastName>5__2;
    public string <street1>5__3;
    public string <street2>5__4;
    public string <city>5__5;
    public string <state>5__6;
    public string <zip>5__7;
    private System.Runtime.CompilerServices.TaskAwaiter <>u__$awaiter8;
    private object <>t__stack;

    void  MoveNext()
    {      
      try
      {      
        IL_0000:  ldc.i4.1
                IL_0001:  stloc.0
                IL_0002:  ldarg.0
                IL_0003:  ldfld      int32 AsyncCaptureVariables.Program/'<Foo>d__0'::'<>1__state'
                IL_0008:  stloc.2
                IL_0009:  ldloc.2
                IL_000a:  ldc.i4.s   -3
                IL_000c:  beq.s      IL_0014

                IL_000e:  ldloc.2
                IL_000f:  ldc.i4.0
                IL_0010:  beq.s      IL_0019

                IL_0012:  br.s       IL_001e

                IL_0014:  br         IL_00ee

                IL_0019:  br         IL_00a8

                IL_001e:  br.s       IL_0020

        //000009:         {
                IL_0020:  nop
        //000010:             var firstName = "Karl";
                IL_0021:  ldarg.0
                IL_0022:  ldstr      "Karl"
                IL_0027:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<firstName>5__1'
        //000011:             var lastName = "Anderson";
                IL_002c:  ldarg.0
                IL_002d:  ldstr      "Anderson"
                IL_0032:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<lastName>5__2'
        //000012:             var street1 = "123 Nowhere Street";
                IL_0037:  ldarg.0
                IL_0038:  ldstr      "123 Nowhere Street"
                IL_003d:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<street1>5__3'
        //000013:             var street2 = "Apt 1-A";
                IL_0042:  ldarg.0
                IL_0043:  ldstr      "Apt 1-A"
                IL_0048:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<street2>5__4'
        //000014:             var city = "Beverly Hills";
                IL_004d:  ldarg.0
                IL_004e:  ldstr      "Beverly Hills"
                IL_0053:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<city>5__5'
        //000015:             var state = "California";
                IL_0058:  ldarg.0
                IL_0059:  ldstr      "California"
                IL_005e:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<state>5__6'
        //000016:             var zip = "90210";
                IL_0063:  ldarg.0
                IL_0064:  ldstr      "90210"
                IL_0069:  stfld      string AsyncCaptureVariables.Program/'<Foo>d__0'::'<zip>5__7'
        //000017: 
        //000018:             await Task.Delay(5000);
                IL_006e:  ldc.i4     0x1388
                IL_0073:  call       class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Delay(int32)
                IL_0078:  callvirt   instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter [mscorlib]System.Threading.Tasks.Task::GetAwaiter()
                IL_007d:  stloc.3
        IL_0078:  callvirt   instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter [mscorlib]System.Threading.Tasks.Task::GetAwaiter()
        IL_007d:  stloc.3
        IL_007e:  ldloca.s   CS$0$0001
        IL_0080:  call       instance bool [mscorlib]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted()
        IL_0085:  brtrue.s   IL_00c6

        IL_0087:  ldarg.0
        IL_0088:  ldc.i4.0
        IL_0089:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_008e:  ldarg.0
        IL_008f:  ldloc.3
        IL_0090:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_0095:  ldarg.0
        IL_0096:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
        IL_009b:  ldloca.s   CS$0$0001
        IL_009d:  ldarg.0
        IL_009e:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter,valuetype AsyncCaptureVariables.Program/<Foo>d__0>(!!0&,
                                                                                                                                                                                                                                                         !!1&)
        IL_00a3:  nop
        IL_00a4:  ldc.i4.0
        IL_00a5:  stloc.0
        IL_00a6:  leave.s    IL_011d

        IL_00a8:  ldarg.0
        IL_00a9:  ldfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_00ae:  stloc.3
        IL_00af:  ldarg.0
        IL_00b0:  ldloca.s   CS$0$0002
        IL_00b2:  initobj    [mscorlib]System.Runtime.CompilerServices.TaskAwaiter
        IL_00b8:  ldloc.s    CS$0$0002
        IL_00ba:  stfld      valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter AsyncCaptureVariables.Program/<Foo>d__0::<>u__$awaiter8
        IL_00bf:  ldarg.0
        IL_00c0:  ldc.i4.m1
        IL_00c1:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_00c6:  ldloca.s   CS$0$0001
        IL_00c8:  call       instance void [mscorlib]System.Runtime.CompilerServices.TaskAwaiter::GetResult()
        IL_00cd:  nop
        IL_00ce:  ldloca.s   CS$0$0001
        IL_00d0:  initobj    [mscorlib]System.Runtime.CompilerServices.TaskAwaiter
//000019: 
//000020:             Console.WriteLine(firstName);
        IL_00d6:  ldarg.0
        IL_00d7:  ldfld      string AsyncCaptureVariables.Program/<Foo>d__0::<firstName>5__1
        IL_00dc:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_00e1:  nop
//000021:             Console.WriteLine(city);
        IL_00e2:  ldarg.0
        IL_00e3:  ldfld      string AsyncCaptureVariables.Program/<Foo>d__0::<city>5__5
        IL_00e8:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_00ed:  nop
//000022:         }
//000023: 
//000024:         public static void Main()
//000025:         {
//000026:             var program = new Program();
//000027:             Task t = program.Foo();
//000028:             t.Wait();
//000029:         }
//000030:     }
//000031: }
        IL_00ee:  leave.s    IL_0108

      }  // end .try
      catch [mscorlib]System.Exception 
      {
        IL_00f0:  stloc.1
        IL_00f1:  ldarg.0
        IL_00f2:  ldc.i4.s   -2
        IL_00f4:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
        IL_00f9:  ldarg.0
        IL_00fa:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
        IL_00ff:  ldloc.1
        IL_0100:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetException(class [mscorlib]System.Exception)
        IL_0105:  nop
        IL_0106:  leave.s    IL_011d

      }  // end handler
      IL_0108:  nop
//000022:         }
      IL_0109:  ldarg.0
      IL_010a:  ldc.i4.s   -2
      IL_010c:  stfld      int32 AsyncCaptureVariables.Program/<Foo>d__0::<>1__state
//000023: 
//000024:         public static void Main()
//000025:         {
//000026:             var program = new Program();
//000027:             Task t = program.Foo();
//000028:             t.Wait();
//000029:         }
//000030:     }
//000031: }
      IL_0111:  ldarg.0
      IL_0112:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
      IL_0117:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetResult()
      IL_011c:  nop
      IL_011d:  nop
      IL_011e:  ret
    } // end of method <Foo>d__0::MoveNext

    .method private hidebysig newslot virtual final 
            instance void  SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine param0) cil managed
    {
      .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
      .override [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
      // Code size       13 (0xd)
      .maxstack  8
      IL_0000:  ldarg.0
      IL_0001:  ldflda     valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder AsyncCaptureVariables.Program/<Foo>d__0::<>t__builder
      IL_0006:  ldarg.1
      IL_0007:  call       instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine)
      IL_000c:  ret
    } // end of method <Foo>d__0::SetStateMachine

  } // end of class <Foo>d__0

Ответ 3

Вы можете создавать области с помощью добавленных фигурных скобок в коде, но всякий раз, когда что-то не упоминается, компилятор все равно будет собирать. сделайте это

{  //start of scope
    var firstName = "Karl";
    var lastName = "Anderson";
    var street1 = "123 Nowhere Street";
    var street2 = "Apt 1-A";
    var city = "Beverly Hills";
    var state = "California";
    var zip = "90210";

    await MyTaskHere();
}//end of scope

Есть другие вещи, которые можно принять во внимание, например, если вы можете создавать методы для логического разделения вещей, чем, на мой взгляд, это лучший вариант. Хотя нет никакого вреда и в этом.