Как вызвать асинхронный метод из геттера или сеттера?

каков был бы самый элегантный способ вызова асинхронного метода из геттера или сеттера в C#?

вот некоторые псевдо-код, чтобы помочь объяснить.

async Task<IEnumerable> MyAsyncMethod()
{
    return await DoSomethingAsync();
}

public IEnumerable MyList
{
    get
    {
         //call MyAsyncMethod() here
    }
}

8 ответов


нет технические причина async свойства не разрешены в C#. Это было целенаправленное дизайнерское решение, потому что "асинхронные свойства" - это оксюморон.

свойства должны возвращать текущие значения; они не должны запускать фоновые операции.

обычно, когда кто-то хочет "асинхронное свойство", то, что они действительно хотят, является одним из них:

  1. асинхронный метод, возвращающий значение. В этом case, измените свойство на async метод.
  2. значение, которое может использоваться в привязке данных, но должно вычисляться / извлекаться асинхронно. В этом случае либо используйте async заводской метод для содержащего объекта или используйте async InitAsync() метод. Значение привязки к данным будет default(T) пока значение не будет вычислено / извлечено.
  3. значение, которое дорого создавать, но должно быть кэшировано для будущего использования. В этом случае используйте 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();
        }
    }