Da ich selbst Compiler geschrieben habe, fühle ich mich qualifiziert, eine logischere Meinung abzugeben, die nicht nur auf einigen Tests basiert.
Heutzutage verwandeln die meisten Compiler den Quellcode in einen AST (Abstract Syntax Tree), mit dem der Quellcode sprachunabhängig dargestellt wird.
AST besteht normalerweise aus Syntaxknoten. Ein Syntaxknoten, der einen Wert erzeugt, wird als Ausdruck bezeichnet, während einer, der nichts erzeugt, eine Anweisung ist.
Angesichts des Codes in der Frage,
if (await first_check() && await second_check())
Betrachten wir also den Testbedingungsausdruck
await first_check() && await second_check()
Der für einen solchen Code erstellte AST lautet wie folgt:
AndExpression:
firstOperand = (
AwaitExpression:
operand = (
MethodInvocationExpression:
name = "first_check"
parameterTypes = []
arguments = []
)
)
secondOperand = (
AwaitExpression:
operand = (
MethodInvocationExpression:
name = "second_check"
parameterTypes = []
arguments = []
)
)
Der AST selbst und die Syntax, mit der ich ihn dargestellt habe, wurden im laufenden Betrieb vollständig erfunden. Ich hoffe, es ist klar. Es sieht so aus, als würde es der StackOverflow-Markup-Engine gefallen, da sie gut aussieht! :) :)
An dieser Stelle muss herausgefunden werden, wie interpretiert wird. Nun, ich kann den meisten Dolmetschern sagen, dass sie Ausdrücke nur hierarchisch bewerten. Daher wird es so ziemlich so gemacht:
Bewerten Sie den Ausdruck await first_check() && await second_check()
Bewerten Sie den Ausdruck await first_check()
Bewerten Sie den Ausdruck first_check()
Löse das Symbol auf first_check
- Ist es eine Referenz? Nein (andernfalls prüfen Sie, ob es auf einen Delegierten verweist.)
- Ist es ein Methodenname? Ja (Ich schließe Dinge wie das Auflösen verschachtelter Bereiche, das Überprüfen, ob sie statisch sind oder nicht, usw. nicht ein, da sie nicht zum Thema gehören und in der Frage nicht genügend Informationen enthalten sind, um diese Details genauer zu untersuchen.)
Argumente auswerten. Da ist niemand. Es soll also eine parameterlose Methode mit dem Namen first_check
aufgerufen werden.
Rufen Sie eine parameterlose Methode mit dem Namen auf, first_check
deren Ergebnis der Wert des Ausdrucks ist first_check()
.
Es wird erwartet, dass der Wert ein Task<T>
oder ist ValueTask<T>
, da dies ein wartender Ausdruck ist.
Auf den Ausdruck "Warten" wird gewartet, um den Wert zu erhalten, den er schließlich erzeugen wird.
Produziert der erste Operand des Ausdrucks und false
? Ja. Der zweite Operand muss nicht ausgewertet werden.
An diesem Punkt wissen wir, dass der Wert von await first_check() && await second_check()
notwendigerweise auch false
so sein wird.
Einige der Überprüfungen, die ich aufgenommen habe, werden statisch durchgeführt (dh zur Kompilierungszeit). Sie dienen jedoch dazu, die Dinge klarer zu machen - es ist unnötig, über die Kompilierung zu sprechen, da wir uns nur mit der Art und Weise befassen, wie Ausdrücke ausgewertet werden.
Das Wesentliche an dieser ganzen Sache ist, dass es C # egal ist, ob der Ausdruck erwartet wird oder nicht - es ist immer noch der erste Operand eines und-Ausdrucks und wird als solcher zuerst ausgewertet. Dann wird nur true
ausgewertet , wenn der zweite Operand erzeugt wird. Andernfalls wird angenommen, dass das Ganze und der Ausdruck so sind false
, wie es nicht anders sein kann.
Dies ist meistens die Art und Weise, wie die überwiegende Mehrheit der Compiler, einschließlich Roslyn (der eigentliche C # -Compiler, der vollständig mit C # geschrieben wurde) und Interpreter funktionieren, obwohl ich einige Implementierungsdetails versteckt habe, die keine Rolle spielen, wie die Art und Weise, wie auf den Ausdruck gewartet wird Ich habe wirklich darauf gewartet, was Sie selbst verstehen können, wenn Sie sich den generierten Bytecode ansehen (Sie können eine Website wie diese verwenden . Ich bin sowieso nicht mit dieser Website verbunden - ich schlage sie nur vor, weil sie Roslyn verwendet und ich denke, es ist eine schöne Werkzeug zu beachten .)
Zur Verdeutlichung ist die Art und Weise, wie Ausdrücke abwarten, ziemlich kompliziert und passt nicht zum Thema dieser Frage. Es würde eine vollständige, getrennte Antwort verdienen, um richtig erklärt zu werden, aber ich halte es nicht für wichtig, da es sich lediglich um ein Implementierungsdetail handelt und der erwartete Ausdruck sich ohnehin nicht anders verhält als normale Ausdrücke.
if
nochawait
Kurzschluss beeinflussen.async
Beeinflussen Sie nicht das Verhalten der Sprache, außer dass Sieawait
die Verwendung zulassen und darauf warten, dass bereits asynchrone Vorgänge ausgeführt werden, ohne sie zu blockieren.