Pertanyaan Dapatkah konstruktor menjadi asinkron?


Saya memiliki proyek Silverlight di mana saya mencoba untuk mengisi beberapa data dalam konstruktor:

public class ViewModel
{
    public ObservableCollection<TData> Data { get; set; }

    async public ViewModel()
    {
        Data = await GetDataTask();
    }

    public Task<ObservableCollection<TData>> GetDataTask()
    {
        Task<ObservableCollection<TData>> task;

        //Create a task which represents getting the data
        return task;
    }
}

Sayangnya, saya mendapatkan kesalahan:

Pengubahnya async tidak berlaku untuk item ini

Tentu saja, jika saya membungkus dalam metode standar dan memanggilnya dari konstruktor:

public async void Foo()
{
    Data = await GetDataTask();
}

itu berfungsi dengan baik. Demikian juga, jika saya menggunakan cara keluar-keluar yang lama

GetData().ContinueWith(t => Data = t.Result);

Itu juga berhasil. Saya hanya bertanya-tanya mengapa kita tidak bisa menelepon await dari dalam konstruktor secara langsung. Mungkin ada banyak sekali kasus tepi dan alasan yang menentangnya, saya hanya tidak bisa memikirkannya. Saya juga mencari-cari penjelasan, tetapi sepertinya tidak dapat menemukannya.


179
2017-11-16 01:19


asal


Jawaban:


Konstruktor bertindak sangat mirip dengan metode yang mengembalikan tipe yang dibangun. Dan async metode tidak dapat mengembalikan jenis apa pun, itu harus berupa "kebakaran dan lupa" void, atau Task.

Jika konstruktor tipe T sebenarnya kembali Task<T>, itu akan sangat membingungkan, saya pikir.

Jika konstruktor async berperilaku dengan cara yang sama seperti async void metode, semacam itu istirahat apa konstruktor dimaksudkan untuk menjadi. Setelah konstruktor kembali, Anda harus mendapatkan objek yang diinisialisasi sepenuhnya. Bukan sebuah objek yang akan benar-benar diinisialisasi pada beberapa titik yang tidak terdefinisi di masa depan. Yaitu, jika Anda beruntung dan inisialisasi async tidak gagal.

Semua ini hanyalah tebakan. Tapi tampaknya bagi saya bahwa memiliki kemungkinan konstruktor async membawa lebih banyak masalah daripada nilainya.

Jika Anda benar-benar menginginkan semantik “api dan lupakan” async void metode (yang harus dihindari, jika mungkin), Anda dapat dengan mudah merangkum semua kode dalam async void metode dan memanggilnya dari konstruktor Anda, seperti yang Anda sebutkan dalam pertanyaan.


148
2017-11-16 01:49



Karena tidak mungkin membuat async constructor, saya menggunakan metode async statis yang mengembalikan instance kelas yang dibuat oleh konstruktor pribadi. Ini tidak elegan tetapi bekerja dengan baik.

   public class ViewModel       
   {       
    public ObservableCollection<TData> Data { get; set; }       

    //static async method that behave like a constructor       
    async public static Task<ViewModel> BuildViewModelAsync()  
    {       
     ObservableCollection<TData> tmpData = await GetDataTask();  
     return new ViewModel(tmpData);
    }       

    // private constructor called by the async method
    private ViewModel(ObservableCollection<TData> Data)
    {
     this.Data=Data;   
    }
   }  

150
2017-09-20 20:39



Masalah Anda sebanding dengan pembuatan objek file dan membuka file. Bahkan ada banyak kelas di mana Anda harus melakukan dua langkah sebelum Anda benar-benar dapat menggunakan objek: buat + Inisialisasi (sering disebut sesuatu yang mirip dengan Open).

Keuntungan dari ini adalah konstruktor dapat ringan, dan fungsi Open dapat melakukan hal yang sulit dan memakan waktu. Fungsi Open ini bahkan bisa async.

Kerugiannya adalah Anda harus mempercayai pengguna kelas Anda bahwa ia akan memanggil Initialize () sebelum ia menggunakan fungsi lain dari kelas Anda. Bahkan jika Anda ingin membuat kelas Anda penuh bukti (bodoh bukti?) Anda harus memeriksa setiap fungsi yang Initialize () telah dipanggil.

Pola untuk membuat ini lebih mudah adalah untuk mendeklarasikan konstruktor pribadi dan membuat fungsi statis publik yang akan membangun objek dan memanggil Initialize () sebelum mengembalikan objek yang dibangun. Dengan cara ini Anda akan tahu bahwa setiap orang yang memiliki akses ke objek telah menggunakan fungsi Initialize.

Contoh ini menunjukkan kelas yang meniru konstruktor async yang Anda inginkan

public MyClass
{
    public static async Task<MyClass> CreateAsync(...)
    {
        MyClass x = new MyClass();
        await x.InitializeAsync(...)
        return x;
    }

    // make sure no one but the Create function can call the constructor:
    private MyClass(){}

    private async Task InitializeAsync(...)
    {
        // do the async things you wanted to do in your async constructor
    }

    public async Task<int> OtherFunctionAsync(int a, int b)
    {
        return await OtherFunctionAsync(a, b);
    }

Penggunaannya adalah sebagai berikut:

public async Task<int> SomethingAsync()
{
    // Create and initialize a MyClass object
    MyClass myObject = await MyClass.CreateAsync(...);

    // call something else
    return await myObject.OtherFunctionAsync(4, 7);
}

20
2017-07-17 08:56



Dalam kasus khusus ini, viewmodel diperlukan untuk meluncurkan tugas dan memberitahukan pandangan setelah selesai. "Async property", bukan "async constructor", sedang dalam urutan.

Saya baru saja merilis AsyncMVVM, yang memecahkan masalah ini dengan tepat (antara lain). Jika Anda menggunakannya, ViewModel Anda akan menjadi:

public class ViewModel : AsyncBindableBase
{
    public ObservableCollection<TData> Data
    {
        get { return Property.Get(GetDataAsync); }
    }

    private Task<ObservableCollection<TData>> GetDataAsync()
    {
        //Get the data asynchronously
    }
}

Anehnya, Silverlight didukung. :)


4
2017-12-31 18:53



jika Anda membuat konstruktor asynchronous, setelah membuat objek, Anda mungkin jatuh ke dalam masalah seperti nilai null, bukan objek contoh. Contohnya;

MyClass instance = new MyClass();
instance.Foo(); // null exception here

Itu sebabnya mereka tidak mengijinkan ini kurasa.


3
2017-11-16 01:28



Saya hanya bertanya-tanya mengapa kita tidak bisa menelepon await dari dalam konstruktor secara langsung.

Saya percaya jawaban singkatnya sederhana: Karena. Tim Bersih belum memprogram fitur ini.

Saya percaya dengan sintaks yang benar ini dapat diimplementasikan dan tidak boleh terlalu membingungkan atau rawan kesalahan. Saya pikir Stephen Cleary posting blog dan beberapa jawaban lain di sini secara implisit menunjukkan bahwa tidak ada alasan mendasar untuk menentangnya, dan lebih dari itu - memecahkan kekurangan itu dengan penyelesaian. Adanya cara kerja yang relatif sederhana ini mungkin merupakan salah satu alasan mengapa fitur ini belum (belum) diimplementasikan.


1
2018-05-03 19:55



memanggil async di konstruktor mungkin menyebabkan deadlock, silakan lihat http://social.msdn.microsoft.com/Forums/en/winappswithcsharp/thread/0d24419e-36ad-4157-abb5-3d9e6c5dacf1

http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx


0
2017-11-07 21:54



Saya tidak akrab dengan kata kunci async (apakah ini khusus untuk Silverlight atau fitur baru dalam versi beta Visual Studio?), Tapi saya rasa saya bisa memberi Anda gambaran mengapa Anda tidak bisa melakukan ini.

Bila saya lakukan:

var o = new MyObject();
MessageBox(o.SomeProperty.ToString());

o tidak dapat dilakukan inisialisasi sebelum baris kode berikutnya berjalan. Instansiasi objek Anda tidak dapat ditetapkan sampai konstruktor Anda selesai, dan membuat konstruktor tidak sinkron tidak akan mengubahnya jadi apa gunanya? Namun, Anda bisa memanggil metode asynchronous dari konstruktor Anda dan kemudian konstruktor Anda bisa menyelesaikan dan Anda akan mendapatkan Instansiasi Anda sementara metode async masih melakukan apa pun yang perlu dilakukan untuk mengatur objek Anda.


-1
2017-11-16 01:29



Anda dapat menggunakan Action di dalam Constructor

 public class ViewModel
    {
        public ObservableCollection<TData> Data { get; set; }
       public ViewModel()
        {              
            new Action(async () =>
            {
                  Data = await GetDataTask();
            }).Invoke();
        }

        public Task<ObservableCollection<TData>> GetDataTask()
        {
            Task<ObservableCollection<TData>> task;
            //Create a task which represents getting the data
            return task;
        }
    }

-2
2018-03-20 08:55



Saya menggunakan trik mudah ini.

public sealed partial class NamePage
{
  private readonly Task _initializingTask;

  public NamePage()
  {
    _initializingTask = Init();
  }

  private async Task Init()
  {
    /*
    Initialization that you need with await/async stuff allowed
    */
  }
}

-2
2017-09-06 15:19



Saya akan menggunakan sesuatu seperti ini.

 public class MyViewModel
    {
            public MyDataTable Data { get; set; }
            public MyViewModel()
               {
                   loadData(() => GetData());
               }
               private async void loadData(Func<DataTable> load)
               {
                  try
                  {
                      MyDataTable = await Task.Run(load);
                  }
                  catch (Exception ex)
                  {
                       //log
                  }
               }
               private DataTable GetData()
               {
                    DataTable data;
                    // get data and return
                    return data;
               }
    }

Ini sedekat saya dapatkan untuk konstruktor.


-2
2018-04-17 19:03