476 Stimmen

Können Konstruktoren asynchron sein?

Ich habe ein Projekt, wo ich versuche, einige Daten in einem Konstruktor zu füllen:

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

Leider erhalte ich eine Fehlermeldung:

Der Modifikator async ist für diesen Artikel nicht gültig

Natürlich, wenn ich eine Standardmethode einpacke und diese vom Konstruktor aus aufrufe:

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

Es funktioniert gut. Ebenso, wenn ich die alte Methode von innen nach außen verwende

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

Das funktioniert auch. Ich habe mich nur gefragt, warum wir nicht anrufen können await direkt aus einem Konstruktor heraus. Es gibt wahrscheinlich viele (auch offensichtliche) Randfälle und Gründe dagegen, mir fallen nur keine ein. Ich habe auch nach einer Erklärung gesucht, aber ich kann keine finden.

3voto

tsemer Punkte 2790

Ich habe mich nur gefragt, warum wir nicht anrufen können. await direkt aus einem Konstruktor heraus.

Ich glaube, die kurze Antwort ist einfach: Weil das .Net-Team diese Funktion nicht programmiert hat.

Ich glaube, mit der richtigen Syntax könnte dies umgesetzt werden und sollte nicht zu verwirrend oder fehleranfällig sein. Ich denke, Stephen Clearys Blog-Beitrag und mehrere andere Antworten hier haben implizit darauf hingewiesen, dass es keinen grundsätzlichen Grund gibt, der dagegen spricht, und - mehr als das - diesen Mangel mit Workarounds gelöst. Das Vorhandensein dieser relativ einfachen Umgehungslösungen ist wahrscheinlich einer der Gründe, warum diese Funktion (noch) nicht implementiert wurde.

1voto

Tealc Wu Punkte 486

1voto

johnsmith Punkte 507

Einige der Antworten beinhalten die Erstellung eines neuen public Methode. Wenn Sie dies nicht tun, verwenden Sie die Lazy<T> Klasse:

public class ViewModel
{
    private Lazy<ObservableCollection<TData>> Data;

    async public ViewModel()
    {
        Data = new Lazy<ObservableCollection<TData>>(GetDataTask);
    }

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

        //Create a task which represents getting the data
        return task.GetAwaiter().GetResult();
    }
}

Zu verwenden Data verwenden Data.Value .

0voto

Sanjay Patel Punkte 946

Sie können Action innerhalb von Constructor verwenden

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

0voto

BionicCode Punkte 18445

C# erlaubt nicht async Konstrukteure. Konstruktoren sind dazu gedacht, nach einer kurzen Initialisierung schnell zurückzukehren. Man erwartet nicht, dass eine Instanz, d.h. der Konstruktor, zurückkehrt, und man will auch nicht darauf warten. Selbst wenn asynchrone Konstruktoren möglich wären, ist ein Konstruktor daher kein Ort für langwierige Operationen oder den Start von Hintergrund-Threads. Der einzige Zweck eines Konstruktors ist die Initialisierung von Instanz- oder Klassenmitgliedern auf einen Standardwert oder die erfassten Konstruktorparameter. Sie erstellen immer die Instanz und rufen dann DoSomething() auf diese Instanz. Asynchrone Operationen sind keine Ausnahme. Die teure Initialisierung von Mitgliedern wird immer aufgeschoben.

Es gibt einige Lösungen, um die Forderung nach async Konstrukteure.

  1. Eine einfache alternative Lösung mit Lazy<T> o AsyncLazy<T> (erfordert die Installation des Microsoft.VisualStudio.Threading Paket über den NuGet-Paketmanager). Lazy<T> ermöglicht es, die Instanziierung oder Zuweisung von teuren Ressourcen aufzuschieben.

    public class OrderService { public List<object> Orders => this.OrdersInitializer.GetValue(); private AsyncLazy<List<object>> OrdersInitializer { get; }

    public OrderService() => this.OrdersInitializer = new AsyncLazy<List<object>>(InitializeOrdersAsync, new JoinableTaskFactory(new JoinableTaskContext()));

    private async Task<List<object>> InitializeOrdersAsync() { await Task.Delay(TimeSpan.FromSeconds(5)); return new List<object> { 1, 2, 3 }; } }

    public static void Main() { var orderService = new OrderService();

    // Trigger async initialization orderService.Orders.Add(4); }

  2. Sie können die Daten mit einer Methode anstelle einer Eigenschaft offenlegen

    public class OrderService { private List<object> Orders { get; set; }

    public async Task<List<object>> GetOrdersAsync() { if (this.Orders == null) { await Task.Delay(TimeSpan.FromSeconds(5)); this.Orders = new List<object> { 1, 2, 3 }; } return this.Orders; } }

    public static async Task Main() { var orderService = new OrderService();

    // Trigger async initialization List<object> orders = await orderService.GetOrdersAsync(); }

  3. Verwenden Sie eine InitializeAsync Methode, die vor der Verwendung der Instanz aufgerufen werden muss

    public class OrderService { private List<object> orders; public List<object> Orders { get { if (!this.IsInitialized) { throw new InvalidOperationException(); } return this.orders; } private set { this.orders = value; } }

    public bool IsInitialized { get; private set; }

    public async Task<List<object>> InitializeAsync() { if (this.IsInitialized) { return; }

    await Task.Delay(TimeSpan.FromSeconds(5));
    this.Orders = new List<object> { 1, 2, 3 };
    this.IsInitialized = true;

    } }

    public static async Task Main() { var orderService = new OrderService();

    // Trigger async initialization await orderService.InitializeAsync(); }

  4. Instanzieren Sie die Instanz, indem Sie die teuren Argumente an den Konstruktor übergeben

    public class OrderService { public List<object> Orders { get; }

    public async Task<List<object>> OrderService(List<object> orders) => this.Orders = orders; }

    public static async Task Main() { List<object> orders = await GetOrdersAsync();

    // Instantiate with the result of the async operation var orderService = new OrderService(orders); }

    private static async Task<List<object>> GetOrdersAsync() { await Task.Delay(TimeSpan.FromSeconds(5)); return new List<object> { 1, 2, 3 }; }

  5. Verwenden Sie eine Fabrikmethode und einen privaten Konstruktor

    public class OrderService { public List<object> Orders { get; set; }

    private OrderServiceBase()
    => this.Orders = new List<object>();

    public static async Task<OrderService> CreateInstanceAsync() { var instance = new OrderService(); await Task.Delay(TimeSpan.FromSeconds(5)); instance.Orders = new List<object> { 1, 2, 3 }; return instance; } }

    public static async Task Main() { // Trigger async initialization
    OrderService orderService = await OrderService.CreateInstanceAsync(); }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X