C# 使用和不使用async声明的lambdas之间有区别吗

C# 使用和不使用async声明的lambdas之间有区别吗,c#,asynchronous,lambda,async-await,C#,Asynchronous,Lambda,Async Await,当lambdas()=>DoSomethingAsync()和async()=>wait DoSomethingAsync()都键入为Func时,两者之间是否有区别?我们应该选择哪一个?什么时候 这里是一个简单的控制台应用程序 using System; using System.Threading.Tasks; namespace asyncDemo { class Program { static void Main(string[] args)

当lambdas
()=>DoSomethingAsync()
async()=>wait DoSomethingAsync()
都键入为
Func
时,两者之间是否有区别?我们应该选择哪一个?什么时候

这里是一个简单的控制台应用程序

using System;
using System.Threading.Tasks;

namespace asyncDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var demo = new AsyncDemo();
            var task = demo.RunTheDemo();
            task.Wait();

            Console.ReadLine();
        }
    }

    public class AsyncDemo
    { 
        public async Task Runner(Func<Task> action)
        {
            Console.WriteLine(DateTime.Now.ToLongTimeString() + " Launching the action");
            await action();
        }

        private async Task DoSomethingAsync(string suffix)
        {
            await Task.Delay(2000);
            Console.WriteLine(DateTime.Now.ToLongTimeString() + " Done something, " + suffix);
        }

        public async Task RunTheDemo()
        {
            await Runner(() => DoSomethingAsync("no await"));
            await Runner(async () => await DoSomethingAsync("with await"));
        }
    }
}
所以在
RunTheDemo
中,对
的两个调用都等待Runner(someLambda)似乎以相同的定时特性执行相同的操作-两者都具有正确的两秒延迟

这两条线都能工作,所以它们完全相等吗?
()=>DoSomethingAsync()
async()=>wait-DoSomethingAsync()
构造之间有什么区别?我们应该选择哪一个?什么时候


这与“在一般情况下我是否应该使用
await
”的问题不同,因为这里我们处理的是异步代码,lambda类型为
Func
,在消费方法中正确地等待。问题涉及如何声明这些lambda,以及该声明的效果。

这是IL Viewer针对这两种方法的输出:

await Runner(() => DoSomethingAsync("no await"));

    .method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task 
'<RunTheDemo>b__5_0'() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() 
  = (01 00 00 00 )
.maxstack 8

// [42 32 - 42 60]
IL_0000: ldarg.0      // this
IL_0001: ldstr        "no await"
IL_0006: call         instance class [mscorlib]System.Threading.Tasks.Task TestClass::DoSomethingAsync(string)
IL_000b: ret
} // end of method CompanyManagementController::'<RunTheDemo>b__5_0'



await Runner(async () => await DoSomethingAsync("with await"));

.method private hidebysig instance class [mscorlib]System.Threading.Tasks.Task 
'<RunTheDemo>b__5_1'() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) 
  = (
    01 00 45 57 65 62 43 61 72 64 2e 43 6f 6e 74 72 // ..TestClass
    6f 6c 6c 65 72 73 2e 43 6f 6d 70 61 6e 79 4d 61 // +<<RunTheDemo>
    6e 61 67 65 6d 65 6e 74 43 6f 6e 74 72 6f 6c 6c // b__5_1>d..
    65 72 2b 3c 3c 52 75 6e 54 68 65 44 65 6d 6f 3e  
    62 5f 5f 35 5f 31 3e 64 00 00                    
  )
  // MetadataClassType(TestClass+<<RunTheDemo>b__5_1>d)
.custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() 
  = (01 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() 
  = (01 00 00 00 )
.maxstack 2
.locals init (
  [0] class TestClass/'<<RunTheDemo>b__5_1>d' V_0,
  [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
)

IL_0000: newobj       instance void TestClass/'<<RunTheDemo>b__5_1>d'::.ctor()
IL_0005: stloc.0      // V_0
IL_0006: ldloc.0      // V_0
IL_0007: ldarg.0      // this
IL_0008: stfld        class TestClass TestClass/'<<RunTheDemo>b__5_1>d'::'<>4__this'
IL_000d: ldloc.0      // V_0
IL_000e: call         valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
IL_0013: stfld        valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0018: ldloc.0      // V_0
IL_0019: ldc.i4.m1    
IL_001a: stfld        int32 TestClass/'<<RunTheDemo>b__5_1>d'::'<>1__state'
IL_001f: ldloc.0      // V_0
IL_0020: ldfld        valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0025: stloc.1      // V_1
IL_0026: ldloca.s     V_1
IL_0028: ldloca.s     V_0
IL_002a: call         instance void [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<class TestClass/'<<RunTheDemo>b__5_1>d'>(!!0/*class TestClass/'<<RunTheDemo>b__5_1>d'*/&)
IL_002f: ldloc.0      // V_0
IL_0030: ldflda       valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'<<RunTheDemo>b__5_1>d'::'<>t__builder'
IL_0035: call         instance class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
IL_003a: ret
} // end of method CompanyManagementController::'<RunTheDemo>b__5_1'
wait Runner(()=>DoSomethingAsync(“无等待”);
.method私有隐藏实例类[mscorlib]System.Threading.Tasks.Task
“b__5_0”(cil管理)
{
.custom instance void[mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [42 32 - 42 60]
IL_0000:ldarg.0//
IL_0001:ldstr“无等待”
IL_0006:调用实例类[mscorlib]System.Threading.Tasks.Task TestClass::DoSomethingAsync(字符串)
IL_000b:ret
}//方法结束CompanyManagementController::'b__5_0'
wait Runner(async()=>wait DoSomethingAsync(“with await”));
.method私有隐藏实例类[mscorlib]System.Threading.Tasks.Task
“b__5_1”(cil管理)
{
.custom instance void[mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(类[mscorlib]System.Type)
= (
01 00 45 57 65 62 43 61 72 64 2e 43 6f 6e 74 72/…测试等级
6f 6c 6c 65 72 73 2e 43 6f 6d 70 61 6e 79 4d 61/+d。。
65 72 2b 3c 3c 52 75 6e 54 68 65 44 65 6d 6f 3e
62 5f 5f 35 5f 31 3e 64 00 00
)
//MetadataClassType(TestClass+d)
.custom instance void[mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor()
= (01 00 00 00 )
.custom instance void[mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 2
.init(
[0]类TestClass/'d'V_0,
[1] valuetype[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
)
IL_0000:newobj实例void TestClass/'d':.ctor()
IL_0005:stloc.0//V_0
IL_0006:ldloc.0//V_0
IL_0007:ldarg.0//
IL_0008:stfld类TestClass TestClass/'d':'4_this'
IL_000d:ldloc.0//V_0
IL_000e:调用valuetype[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
IL_0013:stfld valuetype[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'d':'t_uBuilder'
IL_0018:ldloc.0//V_0
IL_0019:ldc.i4.m1
IL_001a:stfld int32 TestClass/'d':'1_状态'
IL_001f:ldloc.0//V_0
IL_0020:ldfld valuetype[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'d':'t_uBuilder'
IL_0025:stloc.1//V_1
IL_0026:ldloca.s V_1
IL_0028:ldloca.s V_0
IL_002a:调用实例void[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start(!!0/*类TestClass/'d'*/&)
IL_002f:ldloc.0//V_0
IL_0030:ldflda valuetype[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TestClass/'d':'t_uBuilder'
IL_0035:调用实例类[mscorlib]System.Threading.Tasks.Task[mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
IL_003a:ret
}//方法结束CompanyManagementController::'b_u5_1'

第二个是使用异步状态机是的,它们是相同的,但这是一个相当简单的例子。这两者在功能上是等效的,使用
async
时,您只需要(可能,取决于编译器)做更多的工作

了解
async
lambda之所以有用的一个更好的例子是,如果您需要处理一系列异步操作,那毕竟是
wait
的目的:

await Runner(async () => await DoSomethingAsync(await httpClient.Get("www.google.com")));
使用和不使用async声明的lambdas之间有区别吗

是的,有区别。一个是异步lambda,另一个只是返回lambda的任务

异步lambda编译到状态机中,而另一个不编译到状态机中,因此异步lambda具有不同的异常语义,因为异常封装在返回的任务中,不能同步抛出

这与常规方法中存在的差别完全相同。例如,在此异步方法之间:

async Task FooAsync()
{
    await DoSomethingAsync("with await");
}
此任务返回方法:

Task FooAsync()
{
    return DoSomethingAsync("no await");
}
查看这些方法可以更清楚地显示它们之间的差异,但是lambda只是语法上的糖分,实际上被编译成与这些方法行为相同的方法

我们应该选择哪一个?什么时候

这取决于你的品味。使用async关键字生成的状态机性能不如简单地返回任务。然而,在某些情况下,异常语义可能令人惊讶

以这段代码为例:

Hamster hamster = null;
Func<Task> asyncAction = () => FooAsync(hamster.Name);

var task = asyncAction();
try
{
    await task;
}
catch
{
    // handle
}

我个人使用任务返回lambda来表示这些单行表达式lambda,因为它们通常非常简单。但是我的团队在经历了一些极其有害的错误之后,总是使用
async
wait
关键字。

基本上是编译器简化lambda并自动从DoSomething返回任务。。。添加括号,现在它就结束了,您必须执行异步或手动返回任务
Hamster hamster = null;
Func<Task> asyncAction = () => FooAsync(hamster.Name);

var task = asyncAction();
try
{
    await task;
}
catch
{
    // handle
}
Func<Task> asyncAction = async () => await FooAsync(hamster.Name);