478 votes

Les constructeurs peuvent-ils être asynchrones?

J'ai un projet Silverlight où je suis en train de remplir certaines données dans un constructeur:

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

Malheureusement, j'obtiens une erreur:

Le modificateur async n'est pas valide pour cet article

Bien sûr, si je les envelopper dans une méthode standard et l'appeler à partir du constructeur:

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

il fonctionne très bien. De même, si j'utilise l'ancien intérieur de manière

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

Qui travaille trop. Je me demandais juste pourquoi on ne peut pas appeler await à partir de l'intérieur d'un constructeur directement. Il y a probablement beaucoup d' (même évident) les cas limites et les raisons qui militent contre, je ne peux pas penser à tout. J'ai aussi de la recherche autour pour une explication, mais n'arrive pas à en trouver.

515voto

Pierre Poliakoff Points 359

Puisqu'il n'est pas possible de créer un constructeur async, j'utilise une méthode async statique qui renvoie une instance de classe créée par un constructeur privé. Ce n'est pas élégant mais ça fonctionne 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;   
    }
   }  
 

284voto

svick Points 81772

Constructeur actes, de façon très similaire à une méthode retournant le type construit. Et async méthode ne peut retourner n'importe quel type, il doit être "fire and forget" voidou Task.

Si le constructeur de type T effectivement retournés Task<T>, qui serait très confuse, je pense.

Si l'async constructeur se comportaient de la même façon qu'un async void méthode, ce genre de sauts de ce constructeur est censé être. Après le constructeur retourne, vous devriez obtenir un entièrement initialisé objet. Pas un objet qui va être fait correctement initialisé à un moment indéfini dans l'avenir. C'est, si vous avez de la chance et de l'asynchrone de l'initialisation n'est pas en panne.

Tout cela est juste une supposition. Mais il me semble que le fait d'avoir la possibilité d'un async constructeur apporte plus d'ennuis que cela vaut la peine.

Si vous voulez vraiment le "fire and forget" sémantique de l' async void méthodes (qui devrait être évité, si possible), vous pouvez facilement encapsuler tout le code dans un async void méthode et l'appeler à partir de votre constructeur, comme vous l'avez mentionné dans la question.

4voto

Emir Akaydın Points 3969

si vous rendez le constructeur asynchrone, après la création d'un objet, vous risquez de rencontrer des problèmes tels que des valeurs null au lieu d'objets d'instance. Par exemple;

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

C'est pourquoi ils ne permettent pas cela, je suppose.

1voto

Tealc Wu Points 129

-6voto

Brandon Moore Points 3772

Je ne suis pas familier avec le mot-clé async (est-ce spécifique à Silverlight ou une nouvelle fonctionnalité dans la version bêta de Visual Studio?), mais je pense que je peux vous donner une idée de pourquoi vous ne pouvez pas faire cela.

Si je fais:

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

o ne peut pas être fait avant l'initialisation de la prochaine ligne de code s'exécute. Une instanciation de l'objet ne peut pas être assigné jusqu'à ce que votre constructeur est terminé, et rendant le constructeur asynchrone ne changera que si ce serait le point? Cependant, vous pourriez l'appeler une méthode asynchrone à partir de votre constructeur et ensuite votre constructeur pourrait complète et vous obtiendrez votre instanciation tandis que la méthode asynchrone est encore en train de faire ce qu'il doit faire pour l'installation de votre objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X