web-dev-qa-db-fra.com

Comment appeler une méthode asynchrone à partir d'un getter ou d'un setter?

Quel serait le moyen le plus élégant d'appeler une méthode asynchrone à partir d'un getter ou d'un setter en C #?

Voici un pseudo-code pour m'expliquer.

async Task<IEnumerable> MyAsyncMethod()
{
    return await DoSomethingAsync();
}

public IEnumerable MyList
{
    get
    {
         //call MyAsyncMethod() here
    }
}
181
Doguhan Uluca

J'avais vraiment besoin que l'appel provienne de la méthode get, en raison de mon architecture découplée. Je suis donc venu avec la mise en œuvre suivante.

sage: Titre se trouve dans un ViewModel ou un objet que vous pouvez déclarer de manière statique en tant que ressource de page. Si vous le liez, la valeur sera remplie sans bloquer l'interface utilisateur lorsque getTitle () sera renvoyé.

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}
47
Doguhan Uluca

Il n'y a pas de raison technique pour laquelle les propriétés async ne sont pas autorisées en C #. C'était une décision de conception délibérée, parce que "propriétés asynchrones" est un oxymoron.

Les propriétés doivent renvoyer les valeurs actuelles. ils ne devraient pas lancer des opérations en arrière-plan.

Habituellement, lorsque quelqu'un veut une "propriété asynchrone", il souhaite réellement l'une de ces options:

  1. Une méthode asynchrone qui renvoie une valeur. Dans ce cas, remplacez la propriété par une méthode async.
  2. Une valeur qui peut être utilisée dans la liaison de données mais doit être calculée/extraite de manière asynchrone. Dans ce cas, utilisez une méthode d'usine async pour l'objet contenant ou une méthode async InitAsync(). La valeur liée aux données sera default(T) jusqu'à ce que la valeur soit calculée/extraite.
  3. Une valeur coûteuse à créer, mais qui devrait être mise en cache pour une utilisation future. Dans ce cas, utilisez AsyncLazyde mon blog ou bibliothèque AsyncEx . Cela vous donnera une propriété awaitable.

Mise à jour: Je couvre propriétés asynchrones dans l'un de mes derniers articles de blog "asynchrones".

174
Stephen Cleary

Vous ne pouvez pas l'appeler de manière asynchrone, car il n'y a pas de support de propriété asynchrone, seulement des méthodes async. En tant que tel, il existe deux options, toutes deux tirant parti du fait que les méthodes asynchrones du CTP ne sont en réalité qu'une méthode qui renvoie Task<T> ou Task:

// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
    get
    {
         // Just call the method
         return MyAsyncMethod();
    }
}

Ou:

// Make the property blocking
public IEnumerable MyList
{
    get
    {
         // Block via .Result
         return MyAsyncMethod().Result;
    }
}
89
Reed Copsey

Je pense que nous pouvons attendre la valeur en retournant d’abord la valeur null, puis la valeur réelle. Par conséquent, dans le cas de Pure MVVM (projet PCL, par exemple), je pense que la solution suivante est la plus élégante:

private IEnumerable myList;
public IEnumerable MyList
{
  get
    { 
      if(myList == null)
         InitializeMyList();
      return myList;
     }
  set
     {
        myList = value;
        NotifyPropertyChanged();
     }
}

private async void InitializeMyList()
{
   MyList = await AzureService.GetMyList();
}

Je pensais que. GetAwaiter (). GetResult () était exactement la solution à ce problème, non? par exemple:

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            _Title = getTitle().GetAwaiter().GetResult();
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}
5
bc3tech

Puisque votre "propriété async" est dans un modèle de vue, vous pouvez utiliser AsyncMVVM :

class MyViewModel : AsyncBindableBase
{
    public string Title
    {
        get
        {
            return Property.Get(GetTitleAsync);
        }
    }

    private async Task<string> GetTitleAsync()
    {
        //...
    }
}

Il se chargera du contexte de synchronisation et de la notification de changement de propriété pour vous.

1
Dmitry Shechtman

Vous pouvez utiliser Task comme ceci:

public int SelectedTab
        {
            get => selected_tab;
            set
            {
                selected_tab = value;

                new Task(async () =>
                {
                    await newTab.ScaleTo(0.8);
                }).Start();
            }
        }
0
MohsenB