在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>。跑吧,那就没用了。

其他回答

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

你可以这样做:

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

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

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

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.

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

你可以简单地这样做:

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

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

当我遇到这个问题时,试图从setter或构造函数运行异步方法同步会让我陷入UI线程上的死锁,并且使用事件处理程序需要在总体设计中进行太多更改。 解决方案通常是,只显式地写我想隐式发生的事情,也就是让另一个线程处理这个操作,并让主线程等待它完成:

string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();

你可能会说我滥用了这个框架,但它确实有效。