Bagaimana Anda menerapkan metode pendelegasian tindakan async?

132

Sedikit informasi latar belakang.

Saya mempelajari tumpukan API Web dan saya mencoba merangkum semua data dalam bentuk objek "Hasil" dengan parameter seperti Success dan ErrorCodes.

Namun metode yang berbeda, akan menghasilkan hasil yang berbeda dan kode kesalahan tetapi objek hasil umumnya akan dipakai dengan cara yang sama.

Untuk menghemat waktu dan juga untuk mempelajari lebih lanjut tentang kemampuan async / menunggu di C #, saya mencoba untuk membungkus semua metode tubuh dari aksi api web saya dalam delegasi aksi asinkron tetapi terjebak dalam sedikit hambatan ...

Diberikan kelas-kelas berikut:

public class Result
{
    public bool Success { get; set; }
    public List<int> ErrorCodes{ get; set; }
}

public async Task<Result> GetResultAsync()
{
    return await DoSomethingAsync<Result>(result =>
    {
        // Do something here
        result.Success = true;

        if (SomethingIsTrue)
        {
            result.ErrorCodes.Add(404);
            result.Success = false;
        }
    }
}

Saya ingin menulis metode yang melakukan tindakan pada objek Hasil dan mengembalikannya. Biasanya melalui metode sinkron

public T DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    T result = new T();
    resultBody(result);
    return result;
}

Tetapi bagaimana cara mengubah metode ini menjadi metode asinkron menggunakan async / menunggu?

Inilah yang saya coba:

public async Task<T> DoSomethingAsync<T>(Action<T, Task> resultBody) 
    where T: Result, new()
{
    // But I don't know what do do from here.
    // What do I await?
}
Albin Anke
sumber
1
Jika Anda new-ing atas T, mengapa metode kebutuhan Anda untuk menjadi asynchronous? AFAIK dalam kode menggunakan API asinkron, Anda hanya perlu menyebarkan asyncness dari metode lain yang Anda gunakan.
milimoose
Maaf saya masih cukup baru dalam hal ini, apa yang Anda maksud ketika Anda mengatakan bahwa Anda hanya perlu melakukan propagasi, dan apa yang harus dilakukan dengan T baru itu?
Albin Anke
Saya pikir saya sudah mengetahuinya, terima kasih banyak, Anda memberi saya sesuatu untuk dipikirkan.
Albin Anke
1
Mengapa Anda bahkan mencoba melakukan async ini? Lebih sering tidak dalam situasi server web melakukan async palsu dengan membungkus kode sinkron dalam tugas (seperti yang Anda coba lakukan) lebih lambat daripada hanya melakukannya secara sinkron.
Scott Chamberlain
1
@AlbinAnke Dengan "menyebarkan" maksud saya bahwa jika Anda memanggil metode .NET seperti Stream.ReadAsync()dalam suatu metode, metode itu sendiri harus asinkron, dan mengembalikan di Task<T>mana TAnda akan kembali adalah metode yang sinkron. Idenya adalah bahwa dengan cara ini, setiap penelepon metode Anda kemudian dapat "menunggu secara tidak sinkron" (saya tidak tahu apa istilah yang baik untuk ini) untuk mendasari Stream.ReadAsync()untuk menyelesaikan. Metafora untuk ini yang dapat Anda gunakan adalah bahwa async "menular", dan menyebar dari tingkat rendah I / O bawaan ke kode lain yang hasilnya tergantung pada kata I / O tersebut.
millimoose

Jawaban:

307

The asyncsetara Action<T>adalah Func<T, Task>, jadi saya percaya ini adalah apa yang Anda cari:

public async Task<T> DoSomethingAsync<T>(Func<T, Task> resultBody)
    where T : Result, new()
{
  T result = new T();
  await resultBody(result);
  return result;
}
Stephen Cleary
sumber
@Stephen Jelas Saya Mencoba mengimplementasikan beberapa yang serupa ke dalam ligth Messenger MVVM, dapatkah saya menerapkan cara yang sama?
Juan Pablo Gomez
@JuanPabloGomez: Saya tidak terbiasa dengan jenis olahpesan mereka, tapi saya tidak mengerti mengapa itu tidak berhasil.
Stephen Cleary
1
Ini luar biasa! Saya pikir itu tidak mungkin untuk membuat Tindakan async, dan sudah menganggapnya sebagai cacat bahasa. Saya tidak berpikir tentang menggunakan Fungsi. Terima kasih.
Noel Widmer
2
@ DFSFOT: Setara async dari suatu voidmetode adalah Task-mengembalikan metode; dengan demikian, ekuivalen async Actionadalah Func<Task>, dan ekuivalen async Action<T>adalah Func<T, Task>. Info lebih lanjut di sini .
Stephen Cleary
1
@ DFSFOT: Metode async akan kembali Taskketika tidak memiliki nilai kembali. Jika menggunakan asynckata kunci, maka Taskinstance aktual akan dibuat oleh mesin negara, bukan fungsinya secara langsung.
Stephen Cleary
-11

Jadi saya percaya cara untuk mengimplementasikan ini adalah:

public Task<T> DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    return Task<T>.Factory.StartNew(() =>
    {
        T result = new T();
        resultBody(result);
        return result;
    });
}
Albin Anke
sumber
7
Anda harus menghindari Task.Run(dan bahkan lebih lagi StartNew) di ASP.NET.
Stephen Cleary
Apa cara yang lebih baik untuk melakukan ini?
Albin Anke
Saya memposting jawaban, dan juga menjawab @ svick. Keduanya jawaban yang bagus.
Stephen Cleary