如何从getter或setter调用异步方法?

Dog*_*uca 189 c# async-ctp

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

这里有一些伪代码可以帮助解释自己.

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

public IEnumerable MyList
{
    get
    {
         //call MyAsyncMethod() here
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 182

没有技术原因async在C#中不允许使用属性.这是一个有目的的设计决策,因为"异步属性"是矛盾的.

属性应返回当前值; 他们不应该开始背景操作.

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

  1. 一种返回值的异步方法.在这种情况下,将属性更改为async方法.
  2. 可以在数据绑定中使用但必须异步计算/检索的值.在这种情况下,要么使用async工厂方法来包含对象,要么使用async InitAsync()方法.数据绑定值将default(T)一直到计算/检索该值.
  3. 一个创建起来很昂贵的值,但应该缓存以供将来使用.在这种情况下,请使用AsyncLazy 我的博客AsyncEx库.这将为您提供一个有await能力的财产.

更新:我在我最近的一篇"异步OOP"博客文章中介绍了异步属性.

  • 在第 2 点。恕我直言,您没有考虑设置属性应该*再次*初始化基础数据(不仅在构造函数中)的常规场景。除了使用 *Nito AsyncEx* 或使用 `Dispatcher.CurrentDispatcher.Invoke(new Action(..)` 之外,还有其他方法吗? (2认同)
  • @Stephan:好的,会尝试一下。也许有一篇关于这个异步数据绑定视图模型场景的好文章已经到位。例如,绑定到“{Binding PropName.Result}”对我来说并不是一件容易的事。 (2认同)

Ree*_*sey 91

您不能异步调用它,因为没有异步属性支持,只有异步方法.因此,有两个选项,都利用了CTP中的异步方法实际上只是一个返回的方法Task<T>Task:

// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
    get
    {
         // Just call the method
         return MyAsyncMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

要么:

// Make the property blocking
public IEnumerable MyList
{
    get
    {
         // Block via .Result
         return MyAsyncMethod().Result;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @duluca:您也可以尝试使用类似`private async void SetupList(){MyList = await MyAsyncMethod(); 一旦异步操作完成,这将导致MyList被设置(然后自动绑定,如果它实现了INPC)... (3认同)
  • 谢谢您的答复。选项 A:返回任务并不能真正用于约束目的。选项 B:.Result,正如您所提到的,会阻塞 UI 线程(在 Silverlight 中),因此需要在后台线程上执行操作。我会看看我是否能想出一个可行的解决方案。 (2认同)
  • @duluca:这实际上是我建议您做的...但是要意识到,如果您快速多次访问 Title,您当前的解决方案将导致同时多次调用“getTitle()”... (2认同)

Dog*_*uca 48

由于我的解耦架构,我真的需要来自get方法的调用.所以我想出了以下实现.

用法: 标题位于ViewModel或您可以静态声明为页面资源的对象中.绑定到它,当getTitle()返回时,将在不阻止UI的情况下填充值.

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • **updfrom 18/07/2012**在Win8的RP我们应该分派呼叫更改为:*Window.Current.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,异步()=> {标题=等待GetTytleAsync(URL);} );* (9认同)
  • @ChristopherStevenson,我也这么认为,但我不相信是这样的.因为getter是作为一个火来执行而忘记了,所以在完成后不会调用setter,当getter完成激活时不会更新绑定. (7认同)
  • 不,它有并且有竞争条件,但由于'RaisePropertyChanged("Title")',用户不会看到它.它在完成之前返回.但是,完成后你将设置该属性.这会触发PropertyChanged事件.宾德再次获得财产价值. (2认同)
  • 您还应该知道,该异步调用中的任何异常都将被完全吞噬。如果您有应用程序,它们甚至不会到达您未处理的异常处理程序。 (2认同)

Moh*_*enB 11

你可以这样使用Task

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

                new Task(async () =>
                {
                    await newTab.ScaleTo(0.8);
                }).Start();
            }
        }
Run Code Online (Sandbox Code Playgroud)

  • 然后当它抛出异常时它就会进入黑洞 (19认同)

Jua*_*llo 10

我认为我们可以等待只返回第一个null然后获得实际值的值,所以在纯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();
}
Run Code Online (Sandbox Code Playgroud)

  • 非常*怀疑遵循这个建议.观看此视频然后自己动手:https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-处理程序只. (6认同)
  • 这不是生成编译器警告吗?CS4014:没有等待表达式的异步方法调用 (2认同)
  • 你应该避免使用“async void”方法! (2认同)
  • 每一次呼喊都应该有一个明智的回复,@SuperJMN 你能解释一下为什么吗? (2认同)

bc3*_*ech 5

我想.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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这与使用`.Result`阻塞一样 - 它不是异步的,它可能导致死锁. (4认同)

小智 5

在.NET Core中你可以像这样使用它:

InvokeAsync(async () => { await MyAsyncMethod(); });
Run Code Online (Sandbox Code Playgroud)

所以完整的 getter/setter 应该是这样的:

private bool _myBoolProperty;
public bool MyBoolProperty
{
    get { return _myBoolProperty; }
    set
    {
        _myBoolProperty = value;
        InvokeAsync(async () => { await MyAsyncMethod(); });
    }
}
Run Code Online (Sandbox Code Playgroud)