Aszinkron visszatérési típusok (C#)
Az aszinkron metódusok a következő visszatérési típusokkal rendelkezhetnek:
- Taskegy olyan aszinkron metódushoz, amely egy műveletet hajt végre, de nem ad vissza értéket.
- Task<TResult>aszinkron metódushoz, amely egy értéket ad vissza.
void
eseménykezelőhöz.- Bármely olyan típus, amely rendelkezik akadálymentes
GetAwaiter
módszerrel. A metódus által visszaadott objektumnakGetAwaiter
implementálnia kell az interfészt System.Runtime.CompilerServices.ICriticalNotifyCompletion . - IAsyncEnumerable<T>egy aszinkron streamet visszaadó aszinkron metódushoz.
Az aszinkron metódusokról további információt az Aszinkron programozás aszinkron és várakozási (C#) című témakörben talál.
Számos más típus is létezik, amelyek a Windows számítási feladataira vonatkoznak:
- DispatcherOperationa Windowsra korlátozott aszinkron műveletekhez.
- IAsyncActionA UWP-ben az aszinkron műveletek esetében, amelyek nem adnak vissza értéket.
- IAsyncActionWithProgress<TProgress>A UWP aszinkron műveletei esetében, amelyek a jelentés előrehaladását jelentik, de nem adnak vissza értéket.
- IAsyncOperation<TResult>A UWP aszinkron műveleteihez, amelyek értéket adnak vissza.
- IAsyncOperationWithProgress<TResult,TProgress>A UWP aszinkron műveletei esetében, amelyek a folyamat előrehaladását jelentik, és értéket adnak vissza.
Feladat visszaküldési típusa
Az olyan aszinkron metódusok, amelyek nem tartalmaznak utasítást return
, vagy olyan utasítást return
tartalmaznak, amely nem ad vissza operandusokat, általában visszatérési típussal Taskrendelkeznek. Ezek a metódusok akkor térnek vissza void
, ha szinkronban futnak. Ha egy aszinkron metódushoz visszatérési típust Task használ, a hívó metódus operátorral await
felfüggesztheti a hívó befejezését, amíg a hívott aszinkron metódus be nem fejeződik.
Az alábbi példában a WaitAndApologizeAsync
metódus nem tartalmaz utasítást return
, ezért a metódus egy Task objektumot ad vissza. A visszatérési Task
lehetőség WaitAndApologizeAsync
várandós. A Task típus nem tartalmaz tulajdonságot Result
, mert nincs visszatérési értéke.
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
várakozási kifejezés helyett várakozási utasítással várva vár, hasonlóan a szinkron üres visszatérési metódus hívási utasításához. A várakozási operátor alkalmazása ebben az esetben nem hoz létre értéket. Ha egy adott operandusnak a megfelelő operandusaawait
, Task<TResult>a kifejezés T
eredménye await
a következő lesz: . Ha egy operandusnak a megfelelő operandusa await
egy Task, akkor az await
operandus egy utasítás.
A hívásokat WaitAndApologizeAsync
elkülönítheti a várakozási operátor alkalmazásától, ahogy az az alábbi kódban is látható. Ne feledje azonban, hogy a Task
tulajdonság nem rendelkezik tulajdonságokkal Result
, és hogy nem jön létre érték, amikor egy várakozási operátort alkalmaz egy Task
.
Az alábbi kód elválasztja a WaitAndApologizeAsync
metódus meghívását attól, hogy a metódus által visszaadott feladatra várjon.
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);
Tevékenység<TResult> visszatérési típusa
A Task<TResult> visszatérési típus egy olyan aszinkron metódushoz használatos, amely egy olyan visszatérési utasítást tartalmaz, amelyben az operandus .TResult
A következő példában a GetLeisureHoursAsync
metódus egy return
egész számot visszaadó utasítást tartalmaz. A metódusdeklarációnak meg kell adnia a visszatérési típust Task<int>
. Az aszinkron FromResult metódus egy helyőrző egy olyan művelethez, amely egy 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
Ha GetLeisureHoursAsync
a metódus várakozási kifejezéséből ShowTodaysInfo
hívjuk meg, a várakozási kifejezés lekéri a metódus által GetLeisureHours
visszaadott feladatban tárolt egész számértéket (annak értékétleisureHours
). A várakozási kifejezésekről további információt a await (várakozás) című témakörben talál.
Jobban megértheti, hogyan await
kéri le az eredményt a Task<T>
rendszer, ha elválasztja a hívást GetLeisureHoursAsync
az alkalmazástól await
, ahogyan az az alábbi kódban látható. A nem azonnal várt metódushívás GetLeisureHoursAsync
a metódus deklarációjától elvárható módon ad vissza egy Task<int>
értéket. A feladat a példában a getLeisureHoursTask
változóhoz van rendelve. Mivel getLeisureHoursTask
ez egy Task<TResult>, egy típusú TResult
tulajdonságot Result tartalmaz. Ebben az esetben TResult
egy egész számtípust jelöl. Amikor await
alkalmazva getLeisureHoursTask
van, a várakozás kifejezés kiértékeli a Result tulajdonság getLeisureHoursTask
tartalmát. Az érték hozzá van rendelve a ret
változóhoz.
Fontos
A Result tulajdonság egy blokkoló tulajdonság. Ha a tevékenység befejezése előtt megpróbálja elérni, az aktuálisan aktív szál le lesz tiltva, amíg a tevékenység be nem fejeződik, és az érték elérhetővé válik. A legtöbb esetben a tulajdonság közvetlen elérése helyett az értékhez await
kell hozzáférnie.
Az előző példa lekérte a tulajdonság értékét, Result hogy letiltsa a főszálat, hogy a Main
metódus az alkalmazás befejezése előtt ki tudja nyomtatni a message
konzolra.
var getLeisureHoursTask = GetLeisureHoursAsync();
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await getLeisureHoursTask}";
Console.WriteLine(message);
Érvénytelen visszatérési típus
A visszatérési típust aszinkron void
eseménykezelőkben használja, amelyek visszatérési típust void
igényelnek. Az eseménykezelőkön kívül olyan metódusok esetében, amelyek nem adnak vissza értéket, inkább egy Task értéket kell visszaadnia, mert a visszaadott void
aszinkron metódus nem várható meg. Az ilyen metódusok hívóinak a befejezést úgy kell folytatniuk, hogy nem kell megvárni az úgynevezett aszinkron metódus befejezését. A hívónak függetlennek kell lennie az aszinkron metódus által létrehozott értékektől vagy kivételektől.
A void-returning async metódus hívója nem tudja elkapni a metódusból származó kivételeket. Az ilyen kezeletlen kivételek valószínűleg az alkalmazás sikertelenségéhez vezetnek. Ha egy metódus kivételt Task ad vissza, vagy Task<TResult> kivételt ad vissza, a kivételt a visszaadott tevékenység tárolja. A kivétel újra meg fog jelenni, amikor a feladatra vár. Győződjön meg arról, hogy minden kivételt okozó aszinkron metódus visszatérési Task típussal rendelkezik, és Task<TResult> hogy a metódushoz érkező hívások várnak.
Az alábbi példa egy aszinkron eseménykezelő viselkedését mutatja be. A példakódban az aszinkron eseménykezelőnek tudatnia kell a főszállal, hogy mikor fejeződik be. Ezután a fő szál megvárhatja az aszinkron eseménykezelő befejezését, mielőtt kilépne a programból.
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.
Általános aszinkron visszatérési típusok és ValueTask<TResult>
Az aszinkron metódus bármely olyan típust visszaadhat, amely akadálymentes GetAwaiter
metódussal rendelkezik, amely egy váró típusú példányt ad vissza. Emellett a metódusból GetAwaiter
visszaadott típusnak rendelkeznie kell az System.Runtime.CompilerServices.AsyncMethodBuilderAttribute attribútummal. A fordító által beolvasott attribútumokról szóló cikkben vagy a Feladattípus-szerkesztő minta C#-specifikációjában talál további információt.
Ez a funkció kiegészíti a várható kifejezéseket, amelyek az operandusra await
vonatkozó követelményeket írják le. Az általános aszinkron visszatérési típusok lehetővé teszik, hogy a fordító különböző típusú metódusokat hozzon létre async
. Az általános aszinkron visszatérési típusok lehetővé teszik a teljesítmény javítását a .NET-kódtárakban. Task<TResult> Mivel Task referenciatípusok, a teljesítmény szempontjából kritikus útvonalak memóriafoglalása, különösen ha a lefoglalás szoros ciklusokban történik, hátrányosan befolyásolhatja a teljesítményt. Az általánosított visszatérési típusok támogatása azt jelenti, hogy hivatkozástípus helyett egy egyszerű értéktípust adhat vissza a további memóriafoglalások elkerülése érdekében.
A .NET egy általános feladat-visszaadó érték egyszerűsített implementációjaként biztosítja a System.Threading.Tasks.ValueTask<TResult> struktúrát. Az alábbi példa a ValueTask<TResult> struktúra használatával kéri le két kockadobás értékét.
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
Az általános aszinkron visszatérési típus írása speciális forgatókönyv, és speciális környezetekben való használatra van megcélzva. Fontolja meg inkább a Task
, Task<T>
és ValueTask<T>
a típusok használatát, amelyek az aszinkron kód legtöbb forgatókönyvét lefedik.
A C# 10-es és újabb verzióiban az AsyncMethodBuilder
attribútumot alkalmazhatja egy aszinkron metódusra (az aszinkron visszatérési típus deklarációja helyett) az adott típus szerkesztőjének felülbírálásához. Ezt az attribútumot általában a .NET-futtatókörnyezetben megadott másik szerkesztő használatára alkalmazza.
Aszinkron streamek IAsyncEnumerable<T-vel>
Az aszinkron metódus visszaadhat egy aszinkron streamet, amelyet a IAsyncEnumerable<T>következő jelöl: . Az aszinkron stream lehetővé teszi a streamből beolvasott elemek számbavételét, amikor ismétlődő aszinkron hívásokkal rendelkező adattömbökben jönnek létre elemek. Az alábbi példa egy aszinkron metódust mutat be, amely aszinkron streamet hoz létre:
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();
}
}
Az előző példa aszinkron módon olvas be sorokat egy sztringből. Az egyes sorok beolvasása után a kód számba adja a sztring minden egyes szavát. A hívók az utasítással számba vennék az await foreach
egyes szavakat. A metódus akkor vár, amikor aszinkron módon be kell olvasnia a következő sort a forrássztringből.
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: