C# 缩进lambda和嵌套操作

C# 缩进lambda和嵌套操作,c#,lambda,coding-style,task-parallel-library,C#,Lambda,Coding Style,Task Parallel Library,当使用lambdas时,通常是在TPL上,我会在缩进中迷失。。。是否有一些最佳实践来设置此格式?例如,以以下代码为例: Task t1 = factory.StartNew(() => { DoSomething(); } .ContinueWith((t2) => { DoSomethingWhenComplete(); }, TaskContinuationOptions.OnlyOnRanToCompletion) ).ContinueWith((t3) =&g

当使用lambdas时,通常是在TPL上,我会在缩进中迷失。。。是否有一些最佳实践来设置此格式?例如,以以下代码为例:

Task t1 = factory.StartNew(() =>
{
    DoSomething();
}
.ContinueWith((t2) =>
{
    DoSomethingWhenComplete();
}, TaskContinuationOptions.OnlyOnRanToCompletion)
).ContinueWith((t3) =>
{
    DoSomethingOnError();
}, TaskContinuationOptions.OnlyOnFaulted);
  • 我的“缩进”正确吗
  • 在那个例子中,我想执行t1,然后如果它完成了OK,执行t2,在出错时执行t3。但看起来t3是t2的延续,而不是t1。。。我需要在代码中修复什么才能获得正确的行为?我想我在缩进上迷路了,或者遗漏了一些括号
  • 是否有一些最佳实践来设置此格式

    我不知道有什么。我看你的格式还可以(除了下面的注释)。或者,您也可以按照VisualStudio自动格式化(尝试从编辑器/高级菜单格式化文档)

    在那个例子中,我想要执行t1,然后如果finish ok,则执行t2并继续 错误执行t3。但看起来t3是t2的延续,而不是t2的延续 t1。。。我需要修复代码来纠正行为吗?我想我是 在缩进中丢失或缺少一些括号

    您问题中的代码片段甚至无法编译。你可能想要这个:

    Task t1 = factory.StartNew(() =>
    {
        DoSomething();
    });
    
    t1.ContinueWith((t2) =>
    {
        DoSomethingWhenComplete();
    }, TaskContinuationOptions.OnlyOnRanToCompletion);
    
    t1.ContinueWith((t2) =>
    {
        DoSomethingOnError();
    }, TaskContinuationOptions.OnlyOnFaulted);
    
    这可能有效,但您缺少另一个状态:
    onlyonCancelled
    。我宁愿在同一个地方处理
    t1
    的所有完成状态:

    Task t1 = factory.StartNew(() =>
    {
        DoSomething();
    }).ContinueWith((t2) =>
    {
        if (t2.IsCanceled)
            DoSomethingWhenCancelled();
        else if (t2.IsFaulted)
            DoSomethingOnError(t1.Exception);
        else
            DoSomethingWhenComplete();
    });
    
    这可能仍然缺少一件事:您的代码将在没有同步上下文的随机池线程上继续。例如,如果在UI线程上调用
    ContinueWith
    ,则无法访问
    DoSomething*
    方法中的UI。如果这不是您所期望的,请明确指定要继续的任务计划程序:

    Task t1 = factory.StartNew(() =>
    {
        DoSomething();
    }).
    ContinueWith((t2) =>
    {
        if (t1.IsCanceled)
            DoSomethingWhenCancelled();
        else if (t1.IsFaulted)
            DoSomethingOnError(t1.Exception);
        else
            DoSomethingWhenComplete();
    }, TaskScheduler.FromCurrentSynchronizationContext());
    

    如果您需要以.NET 4为目标,但是使用VS201+作为开发环境,考虑使用<代码>异步/等待< /代码>,而不是<代码>继续> 。编写代码更容易,可读性更高,并且可以使用

    try/catch
    进行结构化错误处理

    如果不能使用异步/等待,请考虑使用Stephen Toub的任务链模式(更多细节)。p>

  • 我不确定自己最好的方法是什么。如果我要编写一个与您的代码等价的代码,并且意义变化最小,那么我可能会得到如下结果。对不起,这不是我自己解决的问题:

    Task t1 = Task.Factory.StartNew(
        () =>
        {
            DoSomething();
        })
        .ContinueWith(
            (t2) =>
            {
                DoSomethingWhenComplete();
            },
            TaskContinuationOptions.OnlyOnRanToCompletion)
        .ContinueWith(
            (t3) =>
            {
                DoSomethingOnError();
            },
            TaskContinuationOptions.OnlyOnFaulted);
    
    我使用缩进的原因是,表达式的“子”/“部分”缩进应该比其“父”/“容器”开始的行更深。在前面的几行中,方法的参数是方法调用的一部分。因此,如果方法调用本身处于一个缩进级别,则参数应进一步缩进一个级别:

    MethodCall(
        arg1,
        arg2);
    
    类似地,二元运算符(如作用域解析/成员访问(
    )的两侧都是表达式的子级,我们可以在某种程度上认为它们都处于同一级别。例如,可能存在<代码> A.B.C.<代码>或<代码> A+B+C,并且我认为每个元素都是整体表达式的子元素。因此,由于每个
    .ContinueWith()
    都是从第一行开始的整个语句的一部分,因此它们也应该像下面的多行算术表达式一样缩进:

    var x = 1
        + 2
        + SomethingComplicated();
    
  • 任务并行库的一个要点是使您能够继续编写外观“正常”的代码。您所做的是编写大量代码并调用TPL来重新实现C#-try{}catch{}finally{}块中已经存在的特性。另外,使用Task.Run(),假设您只想将操作提升到线程池(但您可以轻松地将其更改回使用自定义的
    任务工厂

    我在
    catch{}
    中处理错误的方法是调用
    DoSomethingOnError()
    并立即返回,模拟了
    faultedTask.ContinueWith(action,TaskContinuationOptions.OnlyOnFaulted)
    的行为方式。该表达式的结果是表示延续的
    任务。只有当延续本身出现故障时,延续的
    任务才会出现故障。因此,通过将continuation分配给
    t1
    而不是原始的
    任务
    ,您可以有效地捕获并吞咽异常,就像我在
    try{}catch{/code>中捕获并吞咽异常一样。仅仅是我写的lambda就比手动编写一组连续体要清楚得多

    同样,如果在合理的时候使用
    async
    /
    wait
    ,代码会更清晰。如您的问题所示,对于卸载对线程池的一些标准、非异步方法调用,转到
    async
    /
    wait
    实际上对您没有帮助。但是用一个lambda替换一堆tplapi调用看起来确实是一个值得重构的过程


  • 美好的但是考虑移动<代码> StaseNeX<代码>下一行到同一个缩进级别,如<代码>继续> 。@ Listman,我现在要坚持我的丑陋缩进;我试着用我的推理更新我的答案。基本上,我不想在前一行缩进的基础上,将参数缩进一个函数超过一个级别,因为我通常不会在一个更简单的方法调用中进行缩进。我希望所有缩进的连续体都能说明它们是从第1行开始的语句的一部分。
    Task t1 = Task.Run(
        () =>
        {
            try
            {
                DoSomething();
            }
            catch (Exception ex)
            {
                DoSomethingOnError(ex);
                // Error: do not proceed as usual, but do not mark t1
                // as faulted. We did something magical in
                // DoSomethingOnError() that addresses the error so
                // that it doesn’t need to be reported back to our
                // caller.
                return;
            }
    
            // Completed successfully!
            DoSomethingWhenComplete();
        });