Как вызвать метод асинхронизации с геттера или сеттера?
Что было бы самым элегантным способом вызова метода асинхронного метода из getter или setter в С#?
Вот какой-то псевдокод, который поможет мне объяснить.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
Ответы
Ответ 1
Мне действительно нужен был вызов, чтобы начать с метода 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");
}
}
}
Ответ 2
Нет технической причины, по которой async
свойства не разрешены в С#. Это было целенаправленное дизайнерское решение, потому что "асинхронные свойства" - это оксюморон.
Свойства должны возвращать текущие значения; они не должны начинать фоновые операции.
Обычно, когда кто-то хочет "асинхронное свойство", он действительно хочет получить одно из следующих:
- Асинхронный метод, который возвращает значение. В этом случае измените свойство на
async
метод. - Значение, которое может использоваться в привязке данных, но должно вычисляться/извлекаться асинхронно. В этом случае либо используйте
async
метод фабрики для содержащего объекта, либо используйте async InitAsync()
. Значение, связанное с данными, будет использоваться по default(T)
до тех пор, пока значение не будет вычислено/получено. - Значение, которое дорого создать, но должно быть кэшировано для будущего использования. В этом случае используйте
AsyncLazy
из моего блога или библиотеки AsyncEx. Это даст вам await
собственность.
Обновление: я рассматриваю асинхронные свойства в одной из моих последних публикаций в блоге "async OOP".
Ответ 3
Вы не можете называть это асинхронно, поскольку поддержка асинхронных свойств отсутствует, только методы async. Таким образом, есть два варианта, оба из которых используют тот факт, что асинхронные методы в 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;
}
}
Ответ 4
Я думаю, что мы можем ожидать, что значение просто вернет первый нуль, а затем получит реальное значение, поэтому в случае 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();
}
Ответ 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");
}
}
}
Ответ 6
Поскольку ваше свойство "async" находится в режиме просмотра, вы можете использовать AsyncMVVM:
class MyViewModel : AsyncBindableBase
{
public string Title
{
get
{
return Property.Get(GetTitleAsync);
}
}
private async Task<string> GetTitleAsync()
{
//...
}
}
Он позаботится о вашем синхронном контексте и уведомлении об изменении свойства.
Ответ 7
Я думаю, что мой пример ниже может следовать подходу @Stephen-Cleary, но я хотел бы привести закодированный пример. Это для использования в контексте привязки данных, например, Xamarin.
Конструктор класса - или, на самом деле, установщик другого свойства, от которого он зависит, - может вызвать асинхронную пустоту, которая заполнит свойство по завершении задачи без необходимости ожидания или блокировки. Когда он, наконец, получит значение, он обновит ваш пользовательский интерфейс с помощью механизма NotifyPropertyChanged.
Я не уверен в каких-либо побочных эффектах вызова 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();
}
}
Ответ 8
Следует отметить: В случае возвращения результата IEnumerable доходность yield имеет лучшую производительность и не требует преобразования в IEnumerable.
public IEnumerable MyList
{
get
{
yield return MyAsyncMethod();
}
}
Ответ 9
Вы можете изменить свойство на Task<IEnumerable>
и сделать что-то вроде:
get
{
Task<IEnumerable>.Run(async()=>{
return await getMyList();
});
}
и используйте его как ожидание MyList;