I've been playing around with the Visual Studio 14 CTP 2. This version of C# vNext enables the use of the await
keyword inside a finally block.
I am trying to figure out how this was implemented. I know this is an implementation detail and is subject to change until the RTM release, but i still had to pick my brain with this feature.
To try and understand the underlying compiler generated code, i created this sample code:
private async void button1_Click(object sender, EventArgs e)
{
try
{
}
finally
{
await MyFinallyTest();
}
}
private async Task MyFinallyTest()
{
await Task.Delay(1000);
}
This is the compiler generated class:
[CompilerGenerated]
private sealed class <button1_Click>d__1 : IAsyncStateMachine
{
public int <>1__state;
public Form1 <>4__this;
public object <>7__wrap1;
public int <>7__wrap2;
public AsyncVoidMethodBuilder <>t__builder;
public TaskAwaiter <>u__$awaiter0;
private void MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
switch (num)
{
case 1:
break;
default:
{
this.<>7__wrap1 = null;
this.<>7__wrap2 = 0;
try
{
}
catch (object obj2)
{
this.<>7__wrap1 = obj2;
}
awaiter = this.<>4__this.MyFinallyTest().GetAwaiter();
if (awaiter.IsCompleted)
{
goto Label_0096;
}
this.<>1__state = num = 1;
this.<>u__$awaiter0 = awaiter;
Form1.<button1_Click>d__1 stateMachine = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Form1.<button1_Click>d__1>(ref awaiter, ref stateMachine);
return;
}
}
awaiter = this.<>u__$awaiter0;
this.<>u__$awaiter0 = new TaskAwaiter();
this.<>1__state = num = -1;
Label_0096:
awaiter.GetResult();
awaiter = new TaskAwaiter();
object obj3 = this.<>7__wrap1;
if (obj3 != null)
{
Exception source = obj3 as Exception;
if (source <= null)
{
throw obj3;
}
ExceptionDispatchInfo.Capture(source).Throw();
}
int num1 = this.<>7__wrap2;
this.<>7__wrap1 = null;
}
catch (Exception exception2)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception2);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
From what i understand, the compiler is taking the finally
code block and moving it to after the compiler generated catch
block. Something similar to what we had to do if we wanted to await
something in a catch-finally up untill C# 6.0.
I see a couple of things i dont understand:
The compiler is adding a generated
catch
block (which wasn't present in my method) in the form ofcatch (object obj2)
and setting its internalobject
to the obj2 exception. I dont understand why this is being done.The
finally
block which i created no longer exist. Does that mean that any code which isawaited
inside afinally
block doesn't get to "enjoy" the guarantees we get from actually putting code inside such a block?
async-await
feature was released. – Hypno