在c#中从getter或setter调用异步方法的最优雅的方式是什么?
这里有一些伪代码来帮助我解释。
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
在c#中从getter或setter调用异步方法的最优雅的方式是什么?
这里有一些伪代码来帮助我解释。
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
当前回答
因为你的“async属性”在一个视图模型中,你可以使用AsyncMVVM:
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
它将负责同步上下文和属性更改通知。
其他回答
您不能异步调用它,因为没有异步属性支持,只有异步方法。因此,有两个选项,都利用了一个事实,即CTP中的异步方法实际上只是一个返回Task<T>或Task的方法:
// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
get
{
// Just call the method
return MyAsyncMethod();
}
}
Or:
// Make the property blocking
public IEnumerable MyList
{
get
{
// Block via .Result
return MyAsyncMethod().Result;
}
}
当我遇到这个问题时,试图从setter或构造函数运行异步方法同步会让我陷入UI线程上的死锁,并且使用事件处理程序需要在总体设计中进行太多更改。 解决方案通常是,只显式地写我想隐式发生的事情,也就是让另一个线程处理这个操作,并让主线程等待它完成:
string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();
你可能会说我滥用了这个框架,但它确实有效。
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>。跑吧,那就没用了。
你可以简单地这样做:
public IEnumerable MyList
{
get
{
Task.Run(async () => await MyAsyncMethod);
}
}
但根据完整的上下文,你可以尝试找到另一种方式来触发你的异步方法,例如:当你调用MyList时,你可以调用第一个MyAsyncMethod..
我认为我下面的例子可能遵循@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();
}
}