IT story

getter 또는 setter에서 비동기 메소드를 호출하는 방법은 무엇입니까?

hot-time 2020. 5. 15. 08:05
반응형

getter 또는 setter에서 비동기 메소드를 호출하는 방법은 무엇입니까?


C #의 getter 또는 setter에서 비동기 메서드를 호출하는 가장 우아한 방법은 무엇입니까?

자신을 설명하는 데 도움이되는 의사 코드가 있습니다.

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

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

C #에서 속성을 사용할 수 없는 기술적 이유 async는 없습니다. "비동기 특성"이 옥시 모론이기 때문에 의도적 인 설계 결정이었습니다.

속성은 현재 값을 반환해야합니다. 백그라운드 작업을 시작해서는 안됩니다.

일반적으로 누군가 "비동기 속성"을 원할 때 실제로 원하는 것은 다음 중 하나입니다.

  1. 값을 반환하는 비동기 메서드 이 경우 속성을 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 ()이 반환 될 때 UI를 차단하지 않고 값이 채워집니다.

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을 반환하고 실제 값을 얻는 값을 기다릴 수 있다고 생각하므로 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();
}

I thought .GetAwaiter().GetResult() was exactly the solution to this problem, no? eg:

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            _Title = getTitle().GetAwaiter().GetResult();
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}

Since your "async property" is in a viewmodel, you could use AsyncMVVM:

class MyViewModel : AsyncBindableBase
{
    public string Title
    {
        get
        {
            return Property.Get(GetTitleAsync);
        }
    }

    private async Task<string> GetTitleAsync()
    {
        //...
    }
}

It will take care of the synchronization context and property change notification for you.


You can use Task like this :

public int SelectedTab
        {
            get => selected_tab;
            set
            {
                selected_tab = value;

                new Task(async () =>
                {
                    await newTab.ScaleTo(0.8);
                }).Start();
            }
        }

I think my example below may follow @Stephen-Cleary 's approach but I wanted to give a coded example. This is for use in a data binding context for example Xamarin.

The constructor of the class - or indeed the setter of another property on which it is dependent - may call an async void that will populate the property on completion of the task without the need for an await or block. When it finally gets a value it will update your UI via the NotifyPropertyChanged mechanism.

I'm not certain about any side effects of calling a aysnc void from a constructor. Perhaps a commenter will elaborate on error handling etc.

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();
    }


}

You can change the proerty to Task<IEnumerable>

and do something like:

get
{
    Task<IEnumerable>.Run(async()=>{
       return await getMyList();
    });
}

and use it like await MyList;

참고URL : https://stackoverflow.com/questions/6602244/how-to-call-an-async-method-from-a-getter-or-setter

반응형