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

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

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

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

当前回答

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.

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

其他回答

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

你可以这样做:

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

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

你可以像这样使用Task:

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

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

你可以简单地这样做:

public IEnumerable MyList
{
    get
    {
        Task.Run(async () => await MyAsyncMethod);
    }
}

但根据完整的上下文,你可以尝试找到另一种方式来触发你的异步方法,例如:当你调用MyList时,你可以调用第一个MyAsyncMethod..

我认为.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");
        }
    }
}

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

例如:

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