Zaman uyumsuz dönüş türleri (C#)
Zaman uyumsuz yöntemler aşağıdaki dönüş türlerine sahip olabilir:
- Task, bir işlem gerçekleştiren ancak değer döndüren zaman uyumsuz bir yöntem için.
- Task<TResult>, bir değer döndüren zaman uyumsuz bir yöntem için.
void
, bir olay işleyicisi için.- Erişilebilir
GetAwaiter
bir yöntemi olan herhangi bir tür. yöntemi tarafından döndürülen nesne arabiriminiGetAwaiter
System.Runtime.CompilerServices.ICriticalNotifyCompletion uygulamalıdır. - IAsyncEnumerable<T>, zaman uyumsuz akış döndüren zaman uyumsuz bir yöntem için.
Zaman uyumsuz yöntemler hakkında daha fazla bilgi için bkz . Async ve await (C#) ile zaman uyumsuz programlama.
Windows iş yüklerine özgü başka türler de vardır:
- DispatcherOperation, Windows ile sınırlı zaman uyumsuz işlemler için.
- IAsyncAction, UWP'de değer döndürmeyen zaman uyumsuz eylemler için.
- IAsyncActionWithProgress<TProgress>, UWP'de ilerleme durumunu raporlayan ancak değer döndürmeyen zaman uyumsuz eylemler için.
- IAsyncOperation<TResult>, UWP'de bir değer döndüren zaman uyumsuz işlemler için.
- IAsyncOperationWithProgress<TResult,TProgress>, UWP'de ilerleme durumunu raporlayan ve bir değer döndüren zaman uyumsuz işlemler için.
Görev dönüş türü
deyimi içermeyen veya işlenen döndürmeyen bir return
return
deyim içeren zaman uyumsuz yöntemler genellikle dönüş türüne Tasksahiptir. Bu tür yöntemler zaman uyumlu olarak çalıştırılırsa geri döner void
. Zaman uyumsuz bir Task yöntem için dönüş türü kullanırsanız, çağıran yöntem çağrılan zaman uyumsuz yöntem tamamlanana kadar çağıranın tamamlanmasını askıya almak için bir await
işleç kullanabilir.
Aşağıdaki örnekte yöntemi WaitAndApologizeAsync
bir return
deyimi içermediğinden, yöntemi bir Task nesnesi döndürür. döndürülerek Task
WaitAndApologizeAsync
beklenebilir. Dönüş Task değeri olmadığından tür bir Result
özellik içermez.
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
, zaman uyumlu void-returning yöntemi için çağrı deyimine benzer bir await ifadesi yerine await deyimi kullanılarak beklenir. Bu durumda await işlecinin uygulanması bir değer üretmez. bir öğesinin sağ işleneni await
bir Task<TResult>await
olduğunda, ifadesi sonucunu T
verir. bir öğesinin sağ işleneni await
bir Taskawait
olduğunda ve işleneni bir deyimdir.
Aşağıdaki kodda gösterildiği gibi çağrısı WaitAndApologizeAsync
ile await işlecini birbirinden ayırabilirsiniz. Ancak, bir Task
özelliği olmadığını Result
ve bir await işleci bir uygulandığında Task
hiçbir değer üretilmez unutmayın.
Aşağıdaki kod, yöntemini çağırmayı WaitAndApologizeAsync
yöntemin döndürdüğü görevi beklemesinden ayırır.
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);
Görev<TResult> dönüş türü
Task<TResult> dönüş türü, işlenenin TResult
olduğu bir return deyimi içeren zaman uyumsuz bir yöntem için kullanılır.
Aşağıdaki örnekte, GetLeisureHoursAsync
yöntemi tamsayı return
döndüren bir deyim içerir. Yöntem bildiriminin dönüş türünü belirtmesi Task<int>
gerekir. FromResult Zaman uyumsuz yöntem, döndüren bir işlem için yer DayOfWeektutucudur.
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
yöntemindeki ShowTodaysInfo
bir await ifadesinin içinden çağrıldığındaGetLeisureHoursAsync
, await ifadesi yöntemi tarafından GetLeisureHours
döndürülen görevde depolanan tamsayı değerini (değerileisureHours
) alır. await ifadeleri hakkında daha fazla bilgi için bkz . await.
Aşağıdaki kodda gösterildiği gibi çağrısının GetLeisureHoursAsync
uygulamasından await
Task<T>
öğesini ayırarak sonucunun nasıl await
alınabileceğini daha iyi anlayabilirsiniz. Hemen beklenmeyen bir yöntem GetLeisureHoursAsync
çağrısı, yönteminin bildiriminden bekleyebileceğiniz gibi bir Task<int>
döndürür. Görev örnekteki getLeisureHoursTask
değişkene atanır. bir getLeisureHoursTask
olduğundanTask<TResult>, türünde TResult
bir Result özellik içerir. Bu durumda, TResult
bir tamsayı türünü temsil eder. await
öğesine uygulandığında getLeisureHoursTask
await ifadesi özelliğinin ResultgetLeisureHoursTask
içeriğini değerlendirir. Değer değişkenine ret
atanır.
Önemli
Result özelliği bir engelleme özelliğidir. Görevi tamamlanmadan önce erişmeye çalışırsanız, o anda etkin olan iş parçacığı görev tamamlanana ve değer kullanılabilir olana kadar engellenir. Çoğu durumda, özelliğine doğrudan erişmek yerine kullanarak await
değerine erişmeniz gerekir.
Önceki örnekte, yöntemin Result uygulama sona ermeden konsola yazdırabilmesi Main
message
için ana iş parçacığını engellemek için özelliğinin değeri alınmıştı.
var getLeisureHoursTask = GetLeisureHoursAsync();
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await getLeisureHoursTask}";
Console.WriteLine(message);
Geçersiz dönüş türü
Dönüş türünü, dönüş türü gerektiren void
zaman uyumsuz olay işleyicilerinde kullanırsınızvoid
. Değer döndürmeyen olay işleyicileri dışındaki yöntemler için, döndüren zaman uyumsuz bir yöntem void
beklenemediğinden bunun yerine bir döndürmelisinizTask. Böyle bir yöntemin çağıranları, çağrılan zaman uyumsuz yöntemin bitmesi beklenmeden tamamlanmaya devam etmelidir. Çağıran, zaman uyumsuz yöntemin oluşturduğu değerlerden veya özel durumlardan bağımsız olmalıdır.
Void döndüren bir zaman uyumsuz yöntemi çağıran, yönteminden oluşan özel durumları yakalayamaz. Bu tür işlenmeyen özel durumlar uygulamanızın başarısız olmasına neden olabilir. veya döndüren bir TaskTask<TResult> yöntem özel durum oluşturursa, özel durum döndürülen görevde depolanır. Görev beklenirken özel durum yeniden oluşturulur. Özel durum oluşturabilen herhangi bir zaman uyumsuz yöntemin veya dönüş türüne TaskTask<TResult> sahip olduğundan ve yönteme yapılan çağrıların beklendiğinden emin olun.
Aşağıdaki örnek, zaman uyumsuz olay işleyicisinin davranışını gösterir. Örnek kodda, zaman uyumsuz bir olay işleyicisi bittiğinde ana iş parçacığının bunu bilmesi gerekir. Ardından ana iş parçacığı, programdan çıkmadan önce zaman uyumsuz bir olay işleyicisinin tamamlanmasını bekleyebilir.
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.
Genelleştirilmiş zaman uyumsuz dönüş türleri ve ValueTask<TResult>
Zaman uyumsuz yöntem, bir awaiter türünün örneğini döndüren erişilebilir GetAwaiter
bir yönteme sahip olan herhangi bir türü döndürebilir. Ayrıca, yönteminden GetAwaiter
döndürülen tür özniteliğine System.Runtime.CompilerServices.AsyncMethodBuilderAttribute sahip olmalıdır. Derleyici tarafından okunan öznitelikler veya Görev türü oluşturucu düzeni için C# belirtimi makalesinde daha fazla bilgi edinebilirsiniz.
Bu özellik, işleneninin gereksinimlerini açıklayan beklenebilir ifadelerin tamamlayıcısıdır await
. Genelleştirilmiş zaman uyumsuz dönüş türleri, derleyicinin farklı türler döndüren yöntemler oluşturmasını async
sağlar. Genelleştirilmiş zaman uyumsuz dönüş türleri .NET kitaplıklarında performans geliştirmelerini etkinleştirdi. ve Task<TResult> başvuru türleri olduğundanTask, özellikle de ayırmalar sıkı döngülerde gerçekleştiğinde performans açısından kritik yollarda bellek ayırma performansı olumsuz etkileyebilir. Genelleştirilmiş dönüş türleri desteği, ek bellek ayırmalarını önlemek için başvuru türü yerine basit bir değer türü döndürebileceğiniz anlamına gelir.
.NET, System.Threading.Tasks.ValueTask<TResult> genelleştirilmiş bir görev döndüren değerin basit bir uygulaması olarak yapısını sağlar. Aşağıdaki örnek, iki zar atma işleminin değerini almak için yapısını kullanır ValueTask<TResult> .
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
Genelleştirilmiş bir zaman uyumsuz dönüş türü yazmak gelişmiş bir senaryodur ve özel ortamlarda kullanılmak üzere hedeflenmiştir. Bunun yerine, zaman uyumsuz kod için çoğu senaryoyu kapsayan , Task<T>
ve ValueTask<T>
türlerini kullanmayı Task
göz önünde bulundurun.
C# 10 ve sonraki sürümlerde, oluşturucuyu AsyncMethodBuilder
bu tür için geçersiz kılmak için özniteliğini zaman uyumsuz bir yönteme (zaman uyumsuz dönüş türü bildirimi yerine) uygulayabilirsiniz. Bu özniteliği genellikle .NET çalışma zamanında sağlanan farklı bir oluşturucuyu kullanmak için uygularsınız.
IAsyncEnumerable<T ile zaman uyumsuz akışlar>
Zaman uyumsuz bir yöntem tarafından temsil edilen IAsyncEnumerable<T>zaman uyumsuz bir akış döndürebilir. Zaman uyumsuz akış, öğeler yinelenen zaman uyumsuz çağrılarla öbekler halinde oluşturulduğunda bir akıştan okunan öğeleri listelemek için bir yol sağlar. Aşağıdaki örnekte, zaman uyumsuz akış oluşturan zaman uyumsuz bir yöntem gösterilmektedir:
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();
}
}
Yukarıdaki örnek, bir dizedeki satırları zaman uyumsuz olarak okur. Her satır okunduktan sonra, kod dizedeki her sözcüğü numaralandırır. Arayanlar deyimini kullanarak her sözcüğü numaralandırır await foreach
. yöntemi, kaynak dizeden bir sonraki satırı zaman uyumsuz olarak okuması gerektiğinde bekler.