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

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

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

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

当前回答

您可以将属性更改为Task<IEnumerable>

你可以这样做:

get
{
    Task<IEnumerable>.Run(async()=>{
       return await getMyList();
    });
}

然后像这样使用它 等待MyList;

其他回答

您可以将属性更改为Task<IEnumerable>

你可以这样做:

get
{
    Task<IEnumerable>.Run(async()=>{
       return await getMyList();
    });
}

然后像这样使用它 等待MyList;

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

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

我审查了所有的答案,但都有一个性能问题。

例如:

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

Deployment.Current.Dispatcher。InvokeAsync(async () => {Title = await getTitle();});

使用dispatcher不是一个好的答案。

但有一个简单的解决办法,去做就好:

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

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.

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

我认为我下面的例子可能遵循@Stephen-Cleary的方法,但我想给出一个编码的例子。这适用于数据绑定上下文中,例如Xamarin。

类的构造函数——或者它所依赖的另一个属性的setter——可以调用一个async void,该void将在任务完成时填充该属性,而不需要等待或阻塞。当它最终获得一个值时,它将通过NotifyPropertyChanged机制更新你的UI。

我不确定从构造函数调用aysnc void的任何副作用。也许有人会详细说明错误处理等等。

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


}