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 arabirimini GetAwaiterSystem.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:

Görev dönüş türü

deyimi içermeyen veya işlenen döndürmeyen bir returnreturn 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 TaskWaitAndApologizeAsync 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 Tverir. 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 Taskhiç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 TResultolduğ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 awaitTask<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 TResultbir Result özellik içerir. Bu durumda, TResult bir tamsayı türünü temsil eder. await öğesine uygulandığında getLeisureHoursTaskawait ifadesi özelliğinin ResultgetLeisureHoursTaskiç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 Mainmessage 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ı Taskgö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.

Ayrıca bkz.