269 votes

Comment appeler une méthode asynchrone depuis un getter ou un setter ?

Quelle serait la manière la plus élégante d'appeler une méthode asynchrone à partir d'un getter ou d'un setter en C# ?

Voici un pseudo-code pour m'aider à m'expliquer.

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

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

4 votes

Ma question serait de savoir pourquoi. Une propriété est censée imiter quelque chose comme un champ en ce sens qu'elle doit généralement effectuer peu de travail (ou du moins très rapidement). Si vous avez une propriété de longue durée, il vaut mieux l'écrire comme une méthode pour que l'appelant sache qu'il s'agit d'un travail plus complexe.

0 votes

@James : C'est exactement ça - et je soupçonne que c'est la raison pour laquelle cela n'a pas été explicitement pris en charge dans le CTP. Ceci étant dit, vous pouvez toujours rendre la propriété de type Task<T> qui retournera immédiatement, aura une sémantique de propriété normale, et permettra toujours de traiter les choses de manière asynchrone si nécessaire.

19 votes

James Mon besoin découle de l'utilisation de Mvvm et de Silverlight. Je veux pouvoir me lier à une propriété, où le chargement des données se fait paresseusement. La classe d'extension ComboBox que j'utilise exige que la liaison se fasse à l'étape InitializeComponent(), mais le chargement réel des données se fait beaucoup plus tard. En essayant d'accomplir avec aussi peu de code que possible, getter et async semble être la combinaison parfaite.

6voto

bc3tech Points 556

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

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

2voto

Dmitry Shechtman Points 1855

Puisque votre "propriété asynchrone" se trouve dans un modèle de vue, vous pourriez utiliser AsyncMVVM :

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

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

Il s'occupera du contexte de synchronisation et de la notification des changements de propriété pour vous.

0voto

Quandary Points 12867

Nécromancie.
Dans .NET Core/NetStandard2, vous pouvez utiliser la fonction Nito.AsyncEx.AsyncContext.Run au lieu de System.Windows.Threading.Dispatcher.InvokeAsync :

class AsyncPropertyTest
{

    private static async System.Threading.Tasks.Task<int> GetInt(string text)
    {
        await System.Threading.Tasks.Task.Delay(2000);
        System.Threading.Thread.Sleep(2000);
        return int.Parse(text);
    }

    public static int MyProperty
    {
        get
        {
            int x = 0;

            // https://stackoverflow.com/questions/6602244/how-to-call-an-async-method-from-a-getter-or-setter
            // https://stackoverflow.com/questions/41748335/net-dispatcher-for-net-core
            // https://github.com/StephenCleary/AsyncEx
            Nito.AsyncEx.AsyncContext.Run(async delegate ()
            {
                x = await GetInt("123");
            });

            return x;
        }
    }

    public static void Test()
    {
        System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
        System.Console.WriteLine(MyProperty);
        System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
    }

}

Si vous avez simplement choisi System.Threading.Tasks.Task.Run o System.Threading.Tasks.Task<int>.Run alors ça ne marcherait pas.

0voto

Vous pouvez créer un événement et invoquer un événement lorsque la propriété est modifiée. Quelque chose comme ceci :

private event EventHandler<string> AddressChanged;
public YourClassConstructor(){
     AddressChanged += GoogleAddressesViewModel_AddressChanged;
}
private async void GoogleAddressesViewModel_AddressChanged(object sender, string e){
      ... make your async call
}
private string _addressToSearch;
public string AddressToSearch
{
    get { return _addressToSearch; }
    set
    {
        _addressToSearch = value;
        AddressChanged.Invoke(this, AddressToSearch);
    }
}

-1voto

Craig.C Points 136

Je pense que mon exemple ci-dessous peut suivre l'approche de @Stephen-Cleary mais je voulais donner un exemple codé. Il est destiné à être utilisé dans un contexte de liaison de données, par exemple Xamarin.

Le constructeur de la classe - ou bien le setter d'une autre propriété dont elle dépend - peut appeler un void asynchrone qui remplira la propriété à la fin de la tâche sans avoir besoin d'un await ou d'un bloc. Lorsqu'il obtiendra finalement une valeur, il mettra à jour votre interface utilisateur via le mécanisme NotifyPropertyChanged.

Je ne suis pas certain qu'il y ait des effets secondaires à appeler un void aysnc depuis un constructeur. Peut-être qu'un commentateur pourra élaborer sur la gestion des erreurs, etc.

class MainPageViewModel : INotifyPropertyChanged
{
    IEnumerable myList;

    public event PropertyChangedEventHandler PropertyChanged;

    public MainPageViewModel()
    {

        MyAsyncMethod()

    }

    public IEnumerable MyList
    {
        set
        {
            if (myList != value)
            {
                myList = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
                }
            }
        }
        get
        {
            return myList;
        }
    }

    async void MyAsyncMethod()
    {
        MyList = await DoSomethingAsync();
    }

}

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