在c#中从getter或setter调用异步方法的最优雅的方式是什么?

这里有一些伪代码来帮助我解释。

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

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

当前回答

Necromancing。 在。net Core/NetStandard2中,你可以使用Nito.AsyncEx.AsyncContext.Run来代替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"));
    }


}

如果你只是选择System.Threading.Tasks.Task. run或System.Threading.Tasks.Task<int>。跑吧,那就没用了。

其他回答

c#中不允许异步属性并没有技术上的原因。这是一个有目的的设计决策,因为“异步属性”是一个矛盾的说法。

属性应该返回当前值;他们不应该开始幕后行动。

通常,当有人想要一个“异步属性”时,他们真正想要的是以下内容之一:

An asynchronous method that returns a value. In this case, change the property to an async method. A value that can be used in data-binding but must be calculated/retrieved asynchronously. In this case, either use an async factory method for the containing object or use an async InitAsync() method. The data-bound value will be default(T) until the value is calculated/retrieved. A value that is expensive to create, but should be cached for future use. In this case, use AsyncLazy from my blog or AsyncEx library. This will give you an awaitable property.

更新:我在最近的一篇“异步面向对象”博客文章中介绍了异步属性。

我认为.GetAwaiter().GetResult()正是这个问题的解决方案,不是吗? 例如:

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

我认为我们可以等待的值只是返回第一个空,然后得到真实的值,所以在纯MVVM (PCL项目为例)的情况下,我认为下面是最优雅的解决方案:

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

您可以创建事件,并在属性更改时调用事件。 就像这样:

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

因为你的“async属性”在一个视图模型中,你可以使用AsyncMVVM:

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

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

它将负责同步上下文和属性更改通知。