c# - ¿Pueden los constructores ser asíncronos?



5 Answers

Como no es posible hacer un constructor asíncrono, uso un método asíncrono estático que devuelve una instancia de clase creada por un constructor privado. Esto no es elegante pero funciona bien.

   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;   
    }
   }  
c# constructor async-await

Tengo un proyecto de Silverlight en el que intento rellenar algunos datos en un constructor:

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;
    }
}

Desafortunadamente, estoy recibiendo un error:

El modificador async no es válido para este elemento.

Por supuesto, si envuelvo un método estándar y lo llamo desde el constructor:

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

funciona bien. Igualmente, si utilizo el antiguo camino de adentro hacia afuera.

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

Eso también funciona. Me preguntaba por qué no podemos llamar a la await desde dentro de un constructor directamente. Probablemente hay muchos casos de borde (incluso obvios) y razones en contra, simplemente no puedo pensar en ninguno. También busqué una explicación, pero parece que no puedo encontrar ninguna.




En este caso particular, se requiere un viewModel para iniciar la tarea y notificar a la vista una vez finalizada. Una "propiedad asíncrona", no un "constructor asíncrono", está en orden.

Acabo de lanzar AsyncMVVM , que resuelve exactamente este problema (entre otros). Si lo usas, tu ViewModel se convertiría en:

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

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

Curiosamente, Silverlight es compatible. :)







No estoy familiarizado con la palabra clave asíncrona (¿es esto específico de Silverlight o una nueva función en la versión beta de Visual Studio?), Pero creo que puedo darle una idea de por qué no puede hacer esto.

Si lo hago:

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

o no se puede hacer la inicialización antes de que se ejecute la siguiente línea de código. No se puede asignar una instanciación de su objeto hasta que se complete su constructor, y hacer que el constructor sea asíncrono no cambiaría eso, ¿cuál sería el punto? Sin embargo, puede llamar a un método asíncrono desde su constructor y luego su constructor podría completarse y obtendría su instanciación mientras el método asíncrono todavía está haciendo lo que sea necesario para configurar su objeto.




Yo uso este truco fácil.

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
    */
  }
}





Related