Как вызвать асинхронный метод из геттера или сеттера?
каков был бы самый элегантный способ вызова асинхронного метода из геттера или сеттера в 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();
        }
    }
