Как вызвать асинхронный метод из геттера или сеттера?
каков был бы самый элегантный способ вызова асинхронного метода из геттера или сеттера в C#?
вот некоторые псевдо-код, чтобы помочь объяснить.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
8 ответов
нет технические причина async
свойства не разрешены в C#. Это было целенаправленное дизайнерское решение, потому что "асинхронные свойства" - это оксюморон.
свойства должны возвращать текущие значения; они не должны запускать фоновые операции.
обычно, когда кто-то хочет "асинхронное свойство", то, что они действительно хотят, является одним из них:
- асинхронный метод, возвращающий значение. В этом case, измените свойство на
async
метод. - значение, которое может использоваться в привязке данных, но должно вычисляться / извлекаться асинхронно. В этом случае либо используйте
async
заводской метод для содержащего объекта или используйтеasync InitAsync()
метод. Значение привязки к данным будетdefault(T)
пока значение не будет вычислено / извлечено. - значение, которое дорого создавать, но должно быть кэшировано для будущего использования. В этом случае используйте
AsyncLazy
из моего блога или AsyncEx библиотека. Это даст вамawait
способен собственность.
обновление: я свойства асинхронных в одном из моих последних сообщений в блоге "async OOP".
вы не можете вызвать его асинхронно, так как нет поддержки асинхронных свойств, только асинхронные методы. Таким образом, есть два варианта, оба используют тот факт, что асинхронные методы в 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;
}
}
Мне действительно нужен был вызов, исходящий из метода get, из-за моей развязанной архитектуры. Поэтому я придумал следующую реализацию.
использование: заголовок находится в ViewModel или объекте, который вы можете статически объявить как ресурс страницы. Привязка к нему, и значение будет заполнено без блокировки пользовательского интерфейса, когда gettitle () возвращается.
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");
}
}
}
Я думаю, что мы можем дождаться значения, просто возвращающего первый null, а затем получить реальное значение, поэтому в случае чистого MVVM (например, PCL project) Я думаю, что следующее Самое элегантное решение:
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();
}
Я думал .Помощью 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");
}
}
}
поскольку ваше "асинхронное свойство" находится в viewmodel, вы можете использовать AsyncMVVM:
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
Он позаботится о контексте синхронизации и уведомлении об изменении свойств для вас.
вы можете изменить proerty на Task<IEnumerable>
и сделать что-то вроде:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
и использовать его как ждут класс MyList;
одно замечание: в случае, когда возвращается результат IEnumerable, yield return имеет лучшую производительность и не требует преобразования в IEnumerable.
public IEnumerable MyList
{
get
{
yield return MyAsyncMethod();
}
}