web-dev-qa-db-ja.com

ゲッターまたはセッターから非同期メソッドを呼び出す方法は?

C#でゲッターまたはセッターから非同期メソッドを呼び出す最もエレガントな方法は何ですか?

以下に、自分自身を説明するための擬似コードを示します。

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

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

分離されたアーキテクチャのため、getメソッドから発信する呼び出しが本当に必要でした。そこで、次の実装を思いつきました。

使用法:Titleは、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");
        }
    }
}
47
Doguhan Uluca

asyncプロパティがC#で許可されない理由technicalの理由はありません。 「非同期プロパティ」は矛盾した表現なので、意図的な設計決定でした。

プロパティは現在の値を返す必要があります。バックグラウンド操作を開始しないでください。

通常、誰かが「非同期プロパティ」を望んでいるとき、彼らが本当に望んでいるのは次のいずれかです。

  1. 値を返す非同期メソッド。この場合、プロパティをasyncメソッドに変更します。
  2. データバインディングで使用できますが、非同期で計算/取得する必要がある値。この場合、収容オブジェクトに対してasyncファクトリーメソッドを使用するか、async InitAsync()メソッドを使用します。値が計算/取得されるまで、データバインド値はdefault(T)になります。
  3. 作成するのに高価ですが、将来の使用のためにキャッシュする必要がある値。この場合、AsyncLazy私のブログから または AsyncExライブラリ を使用します。これにより、awaitableプロパティが得られます。

更新:最近の「非同期OOP」ブログ投稿の1つで 非同期プロパティ を説明します。

174
Stephen Cleary

非同期プロパティのサポートはなく、非同期メソッドのみであるため、非同期で呼び出すことはできません。そのため、CTPの非同期メソッドは実際にはTask<T>またはTaskを返すメソッドにすぎないという事実を利用する2つのオプションがあります。

// 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;
    }
}
89
Reed Copsey

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

.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");
        }
    }
}
5
bc3tech

「非同期プロパティ」はビューモデルにあるため、 AsyncMVVM を使用できます。

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

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

同期コンテキストとプロパティ変更通知を処理します。

1

次のようにTaskを使用できます。

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

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