Asynchrone retourtypen (C#)
Asynchrone methoden kunnen de volgende retourtypen hebben:
- Task, voor een asynchrone methode die een bewerking uitvoert, maar geen waarde retourneert.
- Task<TResult>, voor een asynchrone methode die een waarde retourneert.
void
, voor een gebeurtenis-handler.- Elk type dat een toegankelijke
GetAwaiter
methode heeft. Het object dat door deGetAwaiter
methode wordt geretourneerd, moet de System.Runtime.CompilerServices.ICriticalNotifyCompletion interface implementeren. - IAsyncEnumerable<T>, voor een asynchrone methode die een asynchrone stroom retourneert.
Zie Asynchrone programmering met asynchroon programmeren met asynchroon en wachten (C#) voor meer informatie over asynchrone methoden.
Er bestaan ook verschillende andere typen die specifiek zijn voor Windows-workloads:
- DispatcherOperation, voor asynchrone bewerkingen beperkt tot Windows.
- IAsyncAction, voor asynchrone acties in UWP die geen waarde retourneren.
- IAsyncActionWithProgress<TProgress>, voor asynchrone acties in UWP die de voortgang rapporteren, maar geen waarde retourneren.
- IAsyncOperation<TResult>, voor asynchrone bewerkingen in UWP die een waarde retourneren.
- IAsyncOperationWithProgress<TResult,TProgress>, voor asynchrone bewerkingen in UWP die de voortgang rapporteren en een waarde retourneren.
Retourtype taak
Asynchrone methoden die geen instructie bevatten return
of die een return
instructie bevatten die geen operand retourneert, hebben meestal een retourtype Task. Dergelijke methoden retourneren void
als ze synchroon worden uitgevoerd. Als u een Task retourtype gebruikt voor een asynchrone methode, kan een aanroepmethode een await
operator gebruiken om de voltooiing van de aanroeper op te schorten totdat de aangeroepen asynchrone methode is voltooid.
In het volgende voorbeeld bevat return
de WaitAndApologizeAsync
methode geen instructie, dus retourneert de methode een Task object. Het retourneren van een Task
mogelijkheid WaitAndApologizeAsync
om te wachten. Het Task type bevat Result
geen eigenschap omdat deze geen retourwaarde heeft.
public static async Task DisplayCurrentInfoAsync()
{
await WaitAndApologizeAsync();
Console.WriteLine($"Today is {DateTime.Now:D}");
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Console.WriteLine("The current temperature is 76 degrees.");
}
static async Task WaitAndApologizeAsync()
{
await Task.Delay(2000);
Console.WriteLine("Sorry for the delay...\n");
}
// Example output:
// Sorry for the delay...
//
// Today is Monday, August 17, 2020
// The current time is 12:59:24.2183304
// The current temperature is 76 degrees.
WaitAndApologizeAsync
wordt verwacht met behulp van een await-instructie in plaats van een await-expressie, vergelijkbaar met de aanroepende instructie voor een synchrone ongeldige retourmethode. De toepassing van een await-operator in dit geval produceert geen waarde. Wanneer de rechteroperand van een await
is, Task<TResult>produceert de await
expressie een resultaat van T
. Wanneer de rechteroperand van een await
een is Task, zijn de await
en de operand een instructie.
U kunt de aanroep WaitAndApologizeAsync
scheiden van de toepassing van een wachtoperator, zoals in de volgende code wordt weergegeven. Houd er echter rekening mee dat een Task
eigenschap niet heeft Result
en dat er geen waarde wordt geproduceerd wanneer een wachtoperator wordt toegepast op een Task
.
Met de volgende code wordt het aanroepen van de WaitAndApologizeAsync
methode gescheiden van het wachten op de taak die door de methode wordt geretourneerd.
Task waitAndApologizeTask = WaitAndApologizeAsync();
string output =
$"Today is {DateTime.Now:D}\n" +
$"The current time is {DateTime.Now.TimeOfDay:t}\n" +
"The current temperature is 76 degrees.\n";
await waitAndApologizeTask;
Console.WriteLine(output);
Retourtype Taak-TResult<>
Het Task<TResult> retourtype wordt gebruikt voor een asynchrone methode die een retourinstructie bevat waarin de operand zich bevindt TResult
.
In het volgende voorbeeld bevat de GetLeisureHoursAsync
methode een return
instructie die een geheel getal retourneert. De declaratie van de methode moet een retourtype opgeven.Task<int>
De FromResult asynchrone methode is een tijdelijke aanduiding voor een bewerking die een DayOfWeek.
public static async Task ShowTodaysInfoAsync()
{
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await GetLeisureHoursAsync()}";
Console.WriteLine(message);
}
static async Task<int> GetLeisureHoursAsync()
{
DayOfWeek today = await Task.FromResult(DateTime.Now.DayOfWeek);
int leisureHours =
today is DayOfWeek.Saturday || today is DayOfWeek.Sunday
? 16 : 5;
return leisureHours;
}
// Example output:
// Today is Wednesday, May 24, 2017
// Today's hours of leisure: 5
Wanneer GetLeisureHoursAsync
wordt aangeroepen vanuit een await-expressie in de ShowTodaysInfo
methode, haalt de await-expressie de gehele waarde (de waarde van leisureHours
) op die is opgeslagen in de taak die door de GetLeisureHours
methode wordt geretourneerd. Zie await voor meer informatie over await-expressies.
U kunt beter begrijpen hoe await
het resultaat wordt opgehaald uit een Task<T>
door de aanroep te GetLeisureHoursAsync
scheiden van de toepassing van await
, zoals in de volgende code wordt weergegeven. Een aanroep naar de methode GetLeisureHoursAsync
die niet onmiddellijk wordt gewacht, retourneert een Task<int>
, zoals u zou verwachten uit de declaratie van de methode. De taak wordt toegewezen aan de getLeisureHoursTask
variabele in het voorbeeld. Omdat getLeisureHoursTask
is een Task<TResult>, het bevat een Result eigenschap van het type TResult
. In dit geval TResult
vertegenwoordigt u een geheel getaltype. Wanneer await
deze wordt toegepast getLeisureHoursTask
op, wordt de await-expressie geëvalueerd op de inhoud van de Result eigenschap van getLeisureHoursTask
. De waarde wordt toegewezen aan de ret
variabele.
Belangrijk
De Result eigenschap is een blokkerende eigenschap. Als u deze probeert te openen voordat de taak is voltooid, wordt de thread die momenteel actief is geblokkeerd totdat de taak is voltooid en de waarde beschikbaar is. In de meeste gevallen moet u toegang krijgen tot de waarde door deze te gebruiken await
in plaats van rechtstreeks toegang te krijgen tot de eigenschap.
In het vorige voorbeeld is de waarde van de Result eigenschap opgehaald om de hoofdthread te blokkeren, zodat de Main
methode de message
naar de console kan afdrukken voordat de toepassing is beëindigd.
var getLeisureHoursTask = GetLeisureHoursAsync();
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await getLeisureHoursTask}";
Console.WriteLine(message);
Ongeldig retourtype
U gebruikt het void
retourtype in asynchrone gebeurtenis-handlers, waarvoor een void
retourtype is vereist. Voor andere methoden dan gebeurtenis-handlers die geen waarde retourneren, moet u in plaats daarvan een Task waarde retourneren, omdat een asynchrone methode die wordt geretourneerd void
, niet kan worden gewacht. Elke aanroeper van een dergelijke methode moet worden voltooid zonder te wachten totdat de aangeroepen asynchrone methode is voltooid. De aanroeper moet onafhankelijk zijn van waarden of uitzonderingen die door de asynchrone methode worden gegenereerd.
De aanroeper van een asynchrone asynchrone methode kan geen uitzonderingen vangen die zijn gegenereerd door de methode. Dergelijke niet-verwerkte uitzonderingen leiden waarschijnlijk tot een storing in uw toepassing. Als een methode die een Task uitzondering retourneert of Task<TResult> genereert, wordt de uitzondering opgeslagen in de geretourneerde taak. De uitzondering wordt opnieuw uitgevoerd wanneer de taak wordt verwacht. Zorg ervoor dat elke asynchrone methode die een uitzondering kan produceren, een retourtype Task heeft of Task<TResult> dat aanroepen naar de methode worden verwacht.
In het volgende voorbeeld ziet u het gedrag van een asynchrone gebeurtenis-handler. In de voorbeeldcode moet een asynchrone gebeurtenishandler de hoofdthread laten weten wanneer deze is voltooid. Vervolgens kan de hoofdthread wachten totdat een asynchrone gebeurtenis-handler is voltooid voordat het programma wordt afgesloten.
public class NaiveButton
{
public event EventHandler? Clicked;
public void Click()
{
Console.WriteLine("Somebody has clicked a button. Let's raise the event...");
Clicked?.Invoke(this, EventArgs.Empty);
Console.WriteLine("All listeners are notified.");
}
}
public class AsyncVoidExample
{
static readonly TaskCompletionSource<bool> s_tcs = new TaskCompletionSource<bool>();
public static async Task MultipleEventHandlersAsync()
{
Task<bool> secondHandlerFinished = s_tcs.Task;
var button = new NaiveButton();
button.Clicked += OnButtonClicked1;
button.Clicked += OnButtonClicked2Async;
button.Clicked += OnButtonClicked3;
Console.WriteLine("Before button.Click() is called...");
button.Click();
Console.WriteLine("After button.Click() is called...");
await secondHandlerFinished;
}
private static void OnButtonClicked1(object? sender, EventArgs e)
{
Console.WriteLine(" Handler 1 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 1 is done.");
}
private static async void OnButtonClicked2Async(object? sender, EventArgs e)
{
Console.WriteLine(" Handler 2 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 2 is about to go async...");
await Task.Delay(500);
Console.WriteLine(" Handler 2 is done.");
s_tcs.SetResult(true);
}
private static void OnButtonClicked3(object? sender, EventArgs e)
{
Console.WriteLine(" Handler 3 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 3 is done.");
}
}
// Example output:
//
// Before button.Click() is called...
// Somebody has clicked a button. Let's raise the event...
// Handler 1 is starting...
// Handler 1 is done.
// Handler 2 is starting...
// Handler 2 is about to go async...
// Handler 3 is starting...
// Handler 3 is done.
// All listeners are notified.
// After button.Click() is called...
// Handler 2 is done.
Gegeneraliseerde asynchrone retourtypen en ValueTask<TResult>
Een asynchrone methode kan elk type retourneren dat een toegankelijke GetAwaiter
methode heeft die een exemplaar van een wachtertype retourneert. Daarnaast moet het type dat wordt geretourneerd uit de GetAwaiter
methode het System.Runtime.CompilerServices.AsyncMethodBuilderAttribute kenmerk hebben. Meer informatie vindt u in het artikel over kenmerken die worden gelezen door de compiler of de C#-specificatie voor het patroon Opbouwfunctie voor taaktypen.
Deze functie is de aanvulling op te wachten expressies, waarin de vereisten voor de operand van await
. Met gegeneraliseerde asynchrone retourtypen kan de compiler methoden genereren async
die verschillende typen retourneren. Gegeneraliseerde asynchrone retourtypen zijn prestatieverbeteringen ingeschakeld in de .NET-bibliotheken. Omdat Task en Task<TResult> verwijzingstypen zijn, kan geheugentoewijzing in prestatiekritieke paden, met name wanneer toewijzingen voorkomen in strakke lussen, de prestaties nadelig beïnvloeden. Ondersteuning voor gegeneraliseerde retourtypen betekent dat u een lichtgewicht waardetype kunt retourneren in plaats van een verwijzingstype om extra geheugentoewijzingen te voorkomen.
.NET biedt de System.Threading.Tasks.ValueTask<TResult> structuur als een lichtgewicht implementatie van een gegeneraliseerde waarde voor het retourneren van taken. In het volgende voorbeeld wordt de ValueTask<TResult> structuur gebruikt om de waarde van twee dobbelstenen op te halen.
class Program
{
static readonly Random s_rnd = new Random();
static async Task Main() =>
Console.WriteLine($"You rolled {await GetDiceRollAsync()}");
static async ValueTask<int> GetDiceRollAsync()
{
Console.WriteLine("Shaking dice...");
int roll1 = await RollAsync();
int roll2 = await RollAsync();
return roll1 + roll2;
}
static async ValueTask<int> RollAsync()
{
await Task.Delay(500);
int diceRoll = s_rnd.Next(1, 7);
return diceRoll;
}
}
// Example output:
// Shaking dice...
// You rolled 8
Het schrijven van een gegeneraliseerd asynchroon retourtype is een geavanceerd scenario en is bedoeld voor gebruik in gespecialiseerde omgevingen. Overweeg in plaats daarvan de Task
, Task<T>
en ValueTask<T>
typen te gebruiken, die betrekking hebben op de meeste scenario's voor asynchrone code.
In C# 10 en hoger kunt u het AsyncMethodBuilder
kenmerk toepassen op een asynchrone methode (in plaats van de declaratie van het asynchrone retourtype) om de opbouwfunctie voor dat type te overschrijven. Normaal gesproken past u dit kenmerk toe om een andere opbouwfunctie te gebruiken die is opgegeven in de .NET-runtime.
Asynchrone streams met IAsyncEnumerable<T>
Een asynchrone methode kan een asynchrone stroom retourneren, vertegenwoordigd door IAsyncEnumerable<T>. Een asynchrone stroom biedt een manier om items op te sommen die uit een stroom worden gelezen wanneer elementen worden gegenereerd in segmenten met herhaalde asynchrone aanroepen. In het volgende voorbeeld ziet u een asynchrone methode waarmee een asynchrone stream wordt gegenereerd:
static async IAsyncEnumerable<string> ReadWordsFromStreamAsync()
{
string data =
@"This is a line of text.
Here is the second line of text.
And there is one more for good measure.
Wait, that was the penultimate line.";
using var readStream = new StringReader(data);
string? line = await readStream.ReadLineAsync();
while (line != null)
{
foreach (string word in line.Split(' ', StringSplitOptions.RemoveEmptyEntries))
{
yield return word;
}
line = await readStream.ReadLineAsync();
}
}
In het voorgaande voorbeeld worden regels asynchroon van een tekenreeks gelezen. Zodra elke regel is gelezen, wordt elk woord in de tekenreeks opgesomd. Bellers zouden elk woord opsommen met behulp van de await foreach
instructie. De methode wacht op het moment dat de volgende regel uit de brontekenreeks asynchroon moet worden gelezen.
Zie ook
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort beschikbaar: In de loop van 2024 zullen we GitHub-problemen geleidelijk uitfaseren als het feedbackmechanisme voor inhoud en deze vervangen door een nieuw feedbacksysteem. Zie voor meer informatie:Feedback verzenden en weergeven voor