7

バックグラウンドでタスクを実行し、完了時に(メインスレッドで)何かを更新する簡単な方法を探しています。低レベルの「モデル」クラスにあるため、NSObject がないため、InvokeOnMainThread を呼び出すことができません。私はこの方法を持っています:

public void GetItemsAsync(Action<Item[]> handleResult)
{
  Item[] items=null;
  Task task=Task.Factory.StartNew(() =>
  {
    items=this.CreateItems(); // May take a second or two
  });
  task.ContinueWith(delegate
  {
    handleResult(items);
  }, TaskScheduler.FromCurrentSynchronizationContext());
}

これはうまくいくように見えますが、

1) これは最善の (最も簡単な) 方法ですか?

2) ローカル変数が心配です:

Item{} items=null

バックグラウンド スレッドが完了する前にメソッドが戻ると、何が消えてなくなるのでしょうか?

ありがとう。

4

4 に答える 4

2

あなたの方法は、あまりにも多くのことをしているので、単一責任の原則に少し違反していると思います。

まず、 :でラップするのではなく、CreateItemsreturnに変更することをお勧めします。TaskGetItemsAsync

public Task<Item[]> CreateItems(CancellationToken token)
{
   return Task.Factory.StartNew(() => 
     // obtaining the data...
     {});
}

CancellationTokenはオプションですが、この長時間実行される操作をキャンセルできる場合に役立ちます。

このメソッドを使用するGetItemsAsyncと、このデリゲートを渡さずにクライアントによる結果を処理するのが非常に簡単であるため、完全に削除できます。

// Somewhere in the client of your class
var task = yourClass.CreateItems(token);
task.ContinueWith(t => 
 // Code of the delegate that previously
 // passed to GetItemsAsync method
 {}, TaskScheduler.FromCurrentSynchronizationContext());

このアプローチを使用すると、1つの責任だけでより明確なコードを取得できます。タスククラス自体は、非同期操作をファーストクラスオブジェクトとして表すための完璧なツールです。提案された手法を使用すると、クライアントコードを変更せずに、単体テストの偽の動作で現在の実装を簡単にモックできます。

于 2012-11-27T10:43:42.203 に答える
2

このようなもの:

    public void GetItemsAsync(Action<Item[]> handleResult)
    {
        int Id = 11;
        Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two
        task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
    }
于 2012-11-27T10:05:41.993 に答える
1

あなたのコードは問題ないようです。

これは、C# 5 を使用できる場合に使用する場合の完璧な例ですが、使用async / awaitできない場合は、継続で行ったように記述する必要があります。

items変数はラムダでキャプチャされるので、それも問題ありません。

外部変数を使用するラムダを記述すると、C# コンパイラはその変数を含むクラスを作成します。これはクロージャーと呼ばれ、ラムダ内の変数にアクセスできることを意味します。

于 2012-11-27T10:04:25.803 に答える
1

これはより良い魂です:

    public void GetItemsAsync(Action<Item[]> handleResult)
    {
        var task = Task.Factory.StartNew<Item[]>(() =>
        {
            return this.CreateItems(); // May take a second or two
        });

        task.ContinueWith(delegate
        {
            handleResult(task.Result);
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }
于 2012-11-27T10:04:25.880 に答える