从C#中的getter或setter调用异步方法最优雅的方法是什么?
这里有一些伪代码可以帮助解释我自己。
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
答案 0 :(得分:164)
没有技术原因导致C#中不允许async
属性。这是一个有目的的设计决策,因为&#34;异步属性&#34;是矛盾的。
属性应返回当前值;他们不应该开始背景操作。
通常,当有人想要一个&#34;异步属性&#34;时,他们真正想要的是其中之一:
async
方法。async
工厂方法,要么使用async InitAsync()
方法。在计算/检索值之前,数据绑定值将为default(T)
。AsyncLazy
from my blog或AsyncEx library。这将为您提供await
能够获得的财产。更新:我在最近的#34; async OOP&#34;中覆盖asynchronous properties。博客文章。
答案 1 :(得分:88)
您不能异步调用它,因为没有异步属性支持,只有异步方法。因此,有两个选项,都利用了CTP中的异步方法实际上只是一个返回Task<T>
或Task
的方法这一事实:
// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
get
{
// Just call the method
return MyAsyncMethod();
}
}
或者:
// Make the property blocking
public IEnumerable MyList
{
get
{
// Block via .Result
return MyAsyncMethod().Result;
}
}
答案 2 :(得分:46)
答案 3 :(得分:11)
我认为我们可以等待只返回第一个null然后获得实际值的值,所以对于Pure 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();
}
答案 4 :(得分: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");
}
}
}
答案 5 :(得分:2)
您可以像这样使用Placeholder properties
:
Action
答案 6 :(得分:1)
由于您的&#34;异步属性&#34;在视图模型中,您可以使用AsyncMVVM:
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
它将为您处理同步上下文和属性更改通知。
答案 7 :(得分:0)
不断创新。
在.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>.Run
,则将无法使用。
答案 8 :(得分:0)
您可以创建一个事件并在属性更改时调用一个事件。 像这样:
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);
}
}
答案 9 :(得分:-1)
我认为下面的示例可以遵循@ Stephen-Cleary的方法,但是我想给出一个编码示例。这用于数据绑定上下文中,例如Xamarin。
该类的构造函数-或实际上是它依赖的另一个属性的setter-可以调用一个异步void,该异步void将在任务完成时填充该属性,而无需等待或阻塞。最终获得值后,它将通过NotifyPropertyChanged机制更新您的UI。
我不确定从构造函数调用aysnc无效的任何副作用。也许评论者会详细说明错误处理等。
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();
}
}
答案 10 :(得分:-1)
当我遇到这个问题时,尝试通过setter或构造函数运行异步方法同步使我陷入UI线程的僵局,并且使用事件处理程序需要在常规设计中进行太多更改。
解决方案通常是明确地写我想要隐式发生的事情,即让另一个线程处理该操作并让主线程等待其完成:
string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();
您可能会说我滥用了该框架,但是它可以工作。
答案 11 :(得分:-1)
我查看了所有答案,但都存在性能问题。
例如:
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()=> {标题=等待getTitle();});
使用调度程序,这不是一个很好的答案。
但是有一个简单的解决方案,只需执行以下操作即可:
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");
}
}
}
答案 12 :(得分:-2)
你可以继续使用它
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
MyAsyncMethod()
.ContinueWith((tsk) =>
{
// what every work
});
return _myList
}
}
答案 13 :(得分:-4)
您可以将礼仪更改为Task<IEnumerable>
并执行以下操作:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
并使用它 等待MyList;