C# 如何从getter或setter调用异步方法?
在C#中,从getter或setter调用异步方法最优雅的方式是什么 这里有一些伪代码来帮助解释我自己C# 如何从getter或setter调用异步方法?,c#,async-ctp,C#,Async Ctp,在C#中,从getter或setter调用异步方法最优雅的方式是什么 这里有一些伪代码来帮助解释我自己 async Task<IEnumerable> MyAsyncMethod() { return await DoSomethingAsync(); } public IEnumerable MyList { get { //call MyAsyncMethod() here } } 异步任务MyAsyncMethod() {
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
异步任务MyAsyncMethod()
{
return wait DoSomethingAsync();
}
公共IEnumerable MyList
{
得到
{
//在此处调用MyAsyncMethod()
}
}
您不能异步调用它,因为没有异步属性支持,只有异步方法。因此,有两种选择,都利用了CTP中的异步方法实际上只是返回
Task
或Task
的方法这一事实:
// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
get
{
// Just call the method
return MyAsyncMethod();
}
}
由于我的解耦体系结构,我确实需要从get方法发起调用。因此,我提出了以下实现 用法:标题位于ViewModel或可以静态声明为页面资源的对象中。绑定到它,当getTitle()返回时,该值将在不阻塞UI的情况下填充 在C#中不允许使用
async
属性,这没有任何技术原因。这是一个有目的的设计决策,因为“异步属性”是一个矛盾修饰法
属性应返回当前值;他们不应该启动后台操作
通常,当有人想要“异步属性”时,他们真正想要的是:
async
方法async
factory方法,或者使用async InitAsync()
方法。在计算/检索值之前,数据绑定值将为默认值(T)AsyncLazy
或。这将为您提供一个await
able属性类MyViewModel:AsyncBindableBase
{
公共字符串标题
{
得到
{
return属性.Get(GetTitleAsync);
}
}
私有异步任务GetTitleAsync()
{
//...
}
}
它将为您处理同步上下文和属性更改通知。我认为我们可以等待值先返回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();
}
您可以将权限更改为
任务
然后做一些类似的事情:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
get
{
Task.Run(异步()=>{
return wait getMyList();
});
}
像这样使用它
等待迈利斯特 我认为我下面的例子可能遵循@Stephen Cleary的方法,但我想给出一个编码的例子。这用于数据绑定上下文,例如Xamarin 类的构造函数(或者实际上是它所依赖的另一个属性的setter)可以调用异步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();
}
}
您可以像这样使用
任务
:
public int SelectedTab
{
get => selected_tab;
set
{
selected_tab = value;
new Task(async () =>
{
await newTab.ScaleTo(0.8);
}).Start();
}
}
当我遇到这个问题时,尝试从setter或构造函数运行异步方法同步性使我在UI线程上陷入死锁,并且使用事件处理程序需要在常规设计中进行太多更改。
解决方案通常是显式编写我希望隐式执行的操作,即让另一个线程处理该操作,并让主线程等待该操作完成:
string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();
你可能会说我滥用了这个框架,但它是有效的。亡灵巫术在.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"));
}
}
类AsyncPropertyTest
{
私有静态异步System.Threading.Tasks.Task GetInt(字符串文本)
{
等待系统。线程。任务。任务。延迟(2000);
系统线程线程睡眠(2000);
返回int.Parse(text);
}
公共静态int-MyProperty
{
得到
{
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(异步委托()
{
x=等待GetInt(“123”);
});
返回x;
}
}
公共静态无效测试()
{
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.Run
,那么它将不起作用 我审查了所有答案,但都存在性能问题
例如:
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(异步()=>{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");
}
}
}
您可以创建事件并在属性更改时调用事件。 大概是这样的:
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);
}
}
私人事件
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"));
}
}
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");
}
}
}
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");
}
}
}
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);
}
}
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
MyAsyncMethod()
.ContinueWith((tsk) =>
{
// what every work
});
}
}