2014-01-30 70 views
3

当通常在TPL上使用lambdas时,我在缩进时丢失了...是否有一些最佳实践来设置它的格式?例如,利用这个代码:缩进lambdas和嵌套操作

Task t1 = factory.StartNew(() => 
{ 
    DoSomething(); 
} 
.ContinueWith((t2) => 
{ 
    DoSomethingWhenComplete(); 
}, TaskContinuationOptions.OnlyOnRanToCompletion) 
).ContinueWith((t3) => 
{ 
    DoSomethingOnError(); 
}, TaskContinuationOptions.OnlyOnFaulted); 
  1. 是我在 “缩进” 正确吗?
  2. 在那个例子中,我想要执行t1,然后如果它完成OK,执行t2,并在出错时执行t3。但看起来t3是从t2开始的延续,而不是从t1开始......我需要在代码中修正哪些问题才能获得正确的行为?我觉得我失去了对压痕或缺少一些括号...

回答

5

有格式化一些这方面的最佳做法?

我不知道任何。我的格式设置看起来不错(除了下面的注释)。或者,您可以按照Visual Studio自动格式化(尝试从编辑器/高级菜单中格式化文档)。

在那个例子中,我想要执行t1,然后如果完成ok请执行t2并且在 错误执行t3。但看起来T3是从t2延续,而不是从 t1 ...我需要修复该代码以纠正行为?我认为我是 在该缩进上丢失或缺少一些括号...

您问题的代码段甚至不会编译。你可能想这一点:

Task t1 = factory.StartNew(() => 
{ 
    DoSomething(); 
}); 

t1.ContinueWith((t2) => 
{ 
    DoSomethingWhenComplete(); 
}, TaskContinuationOptions.OnlyOnRanToCompletion); 

t1.ContinueWith((t2) => 
{ 
    DoSomethingOnError(); 
}, TaskContinuationOptions.OnlyOnFaulted); 

这可以工作,但你错过了另一种状态:OnlyOnCanceled。我宁愿处理的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.0,但使用VS2012 +作为开发环境,可以考虑使用async/awaitMicrosoft.Bcl.Async而不是ContinueWith。代码更容易编写,更具可读性,并可为您提供try/catch的结构化错误处理。

如果您不能使用异步/等待,请考虑使用Stephen Toub的Task.Then模式进行任务链接(更多详细信息here)。

2
  1. 我不确定自己是做缩进的最好方法。如果我要写一个与你的代码等价的意思最小的变化,我可能结束了类似下面的东西。对不起,这不是我已经解决了自己的问题:

    Task t1 = Task.Factory.StartNew(
        () => 
        { 
         DoSomething(); 
        }) 
        .ContinueWith(
         (t2) => 
         { 
          DoSomethingWhenComplete(); 
         }, 
         TaskContinuationOptions.OnlyOnRanToCompletion) 
        .ContinueWith(
         (t3) => 
         { 
          DoSomethingOnError(); 
         }, 
         TaskContinuationOptions.OnlyOnFaulted); 
    

    我我使用的缩进背后的原因是,“孩子” /表达式的“一部分”应比行缩进更深的地方它的“父“/”容器“开始。在前面的行中,该方法的参数是方法调用的一部分。因此,如果该方法调用本身是缩进的一个电平,该参数应该被一层进一步缩进:

    MethodCall(
        arg1, 
        arg2); 
    

    同样,二元运算符的两侧如范围分辨率/成员访问(.)是儿童在某种程度上,我们可以认为他们都处于同一水平。例如,可能有a.b.ca + b + c,我认为每个元素都是整个表达式的子元素。因此,由于每个.ContinueWith()是开始的第一行整体语句的一部分,他们应该也各缩进就像在下面的多算术表达式:

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

    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(); 
        }); 
    

    catch{}通过调用DoSomethingOnError()并立即返回处理错误的方式模拟方式faultedTask.ContinueWith(action, TaskContinuationOptions.OnlyOnFaulted)的行为。该表达式的结果是Task,代表延续。如果继续本身出错,继续的Task只会发生故障。因此,通过将延续分配给t1而不是原始的Task,可以有效地捕捉和吞咽异常,就像我在try{}catch{}中捕捉并吞下异常一样。只是我写的lambda比尝试手动编写一系列延续更清晰。

    作为@Noseratio says,如果您在使用async/await时有意义,则代码会更清晰。为了卸载一些标准的,非异步方法调用到你的问题建议的线程池中,移动到async/await实际上可以帮助你。但用一个lambda替换一堆TPL API调用似乎是一个有价值的重构。

+0

不错!但考虑将'StartNew'下一行移至与'ContinueWith'相同的缩进级别。 – Lightman

+0

@Lightman我现在要坚持我的丑陋缩进;-)。我尝试用我的推理更新我的答案。基本上,我不想将参数缩进超过前一行缩进的一个以上的级别,因为我通常不会进行简单的方法调用。我希望所有的延续都要缩进以澄清它们是第一行开始的声明的一部分。 – binki