2014-11-12 7 views
14

Ich habe einen Dienst können sagen,Await oder Task.FromResult

public interface ISomeService 
{ 
    Task<bool> DoSomeExpensiveCheckAsync(string parameter); 
} 

Und ich habe diese Klasse den Dienst zu verbrauchen. Es muss nur einige einfache Null-Prüfungen durchführen und dann die Service-Antwort zurückgeben.

public class SomeServiceConsumer 
{ 
    private readonly ISomeService _serviceClient; 

    public SomeServiceConsumer(ISomeService serviceClient) 
    { 
     _serviceClient = serviceClient; 
    } 

    public async Task<bool> DoSomething1Async(string someParameter) 
    { 
     if (string.IsNullOrWhiteSpace(someParameter)) 
     { 
      return false; 
     } 
     return await _serviceClient.DoSomeExpensiveCheckAsync(someParameter); 
    } 

    //No async or await keywords 
    public Task<bool> DoSomething2Async(string someParameter) 
    { 
     if (string.IsNullOrWhiteSpace(someParameter)) 
     { 
      return Task.FromResult(false); 
     } 
     return _serviceClient.DoSomeExpensiveCheckAsync(someParameter); 
    } 
} 

Sollte ich DoSomething1Async oder DoSomething2Async?

Nach this answer, sollte ich nicht mit einer unnötigen await einzuwickeln, aber dann muss ich Task.FromResult(false) verwenden für kurzzuschließen wie in DoSomething2Async

Aber nach this answer gibt es Fälle mit try/catch und using Aussagen, wo ich sollte eigentlich await vor Rückkehr.

Bin ich korrigieren dann sagen, dass

  1. Wenn ich try/catch oder using verwenden müssen dann sollte ich await

  2. Sonst nicht await tun, wenn Sie zurückkehren werden. Und verwenden Sie Task.FromResult für Kurzschlüsse

Ich mag DoSomething1Async mehr, und wollen tun, dass überall, wenn jemand sagt, dass es Sache :) tut.

Antwort

13

Wenn Sie darüber besorgt sind, zwischenzuspeichern die Task:

static readonly Task<bool> falseTask = Task.FromResult(false); 

Das async Schlüsselwort auch Ausnahmen wickelt zusammen mit einem richtigen Stack-Trace in der zurück Task, auf. Es ist ein Kompromiss, Sicherheit des Verhaltens für Perf.

Lässt sich die Differenz Szenarien aussehen, wo jeder anders sein würde:

async Task UseSomething1Async(string someParameter) 
{ 
    // if IsNullOrWhiteSpace throws an exception, it will be wrapped in 
    // the task and not thrown here. 
    Task t1 = DoSomething1Async(someParameter); 

    // rather, it'll get thrown here. this is best practice, 
    // it's what users of Task-returning methods expect. 
    await t1; 

    // if IsNullOrWhiteSpace throws an exception, it will 
    // be thrown here. users will not expect this. 
    Task t2 = DoSomething2Async(someParameter); 

    // this would never have been reached. 
    await t2; 
} 

einfach den Punkt hier darstellt - IsNullOrWhiteSpace eigentlich keine Ausnahmen aus irgendeinem Grund werfen.

Soweit Stack-Spuren gehen, Async-Stack-Spuren werden durch wo Sie await bestimmt. No await bedeutet, dass die Methode aus dem Stack-Trace verschwindet.

Sagen DoSomeExpensiveCheckAsync löst eine Ausnahme aus. Im Fall von DoSomething1Async wird der Stack-Trace wie caller -> DoSomething1Async -> DoSomeExpensiveCheckAsync aussehen.

Im Fall von DoSomething2Async würde die Stapelüberwachung wie caller -> DoSomeExpensiveCheckAsync aussehen. Abhängig von der Komplexität Ihres Codes kann dies die Fehlersuche erschweren.

In der Praxis werde ich in der Regel nur direkt eine Task zurückgeben, wenn ich wusste, dass davor keine Ausnahmen ausgelöst werden würden, und wenn der Methodenname lediglich eine Überladung war, die an eine andere Überladung weitergeleitet wurde.Es gibt immer Ausnahmen von dieser Regel. Es gibt Orte, an denen Sie die Leistung maximieren möchten. Wähle und wähle sorgfältig, realisiere, dass du das Leben von dir und deinem Benutzer härter gestalten kannst.

+0

Dank Cory. Ich verstehe nicht, wie es die Sicherheit des Verhaltens beeinflusst. Siehe meinen Kommentar zu @ I3arnons Antwort. – labroo

+0

Ich habe in einer Erklärung bearbeitet. –

4

Es ist nicht wirklich wichtig. Wenn Sie sich dafür entscheiden, Task -Methoden immer mit dem Schlüsselwort async zu markieren, dann gehen Sie voran und verwenden Sie DoSomething1.

Wie Sie gesagt haben, es ist ein Kompromiss:

  • DoSomething2 nicht die Zustandsmaschine für eine async Verfahren benötigt generiert und es ist so leicht schneller (aber der Unterschied ist meist vernachlässigbar).

  • Auf der anderen Seite kann es einige unvorhergesehene Nebenwirkungen in Bezug auf die Ausnahmebehandlung haben, da in einem async Methode die Ausnahme in der zurück Task gespeichert würden und in der anderen wäre es regelmäßig geworfen werden.

+0

Ich verstehe nicht die Ausnahme Unterschied @ I3arnon. Wenn ich DoSomeExpensiveCheckAsync im Dienst eine explizite Ausnahme ausspreche, dann "var from1 = awaitServiceCustomer.DoSomething1Async (" Hello ");" und " var from2 = warten auf someServiceCustomer.DoSomething2Async (" Hello ");" Werfen Sie die genau gleiche Ausnahme. DoSomething1Async hat jedoch einen zusätzlichen TaskAwaiter.ThrowForNonSuccess im Stacktrace. Das zeigt, dass DoSomething1Async etwas extra tut, aber ich bekomme nicht den unvorhergesehenen Nebeneffektteil. – labroo

+0

@labroo Um den Unterschied zu verstehen, müssen Sie überlegen, was ohne '' erwarten würde: 'var task = DoSomethingAsync (" Hello ")'. Wenn es eine Ausnahme gibt und die Methode async ist, würde dies keine Ausnahme auslösen. Die Ausnahme würde in der Aufgabe gespeichert und auf "warten" neu gestartet werden. Wenn die Methode nicht asynchron ist, wird die Ausnahme wie jede andere Methode ausgelöst. – i3arnon

+0

@labroo Hier finden Sie weitere Informationen: http://StackOverflow.com/a/24441859/885318 – i3arnon

Verwandte Themen