2012-11-27 32 views
7

私はバックグラウンドでタスクを実行し、完了したら(メインスレッド上で)何かを更新する簡単な方法を探しています。それは低レベルの 'モデル'クラスなので、NSObjectを持っていないのでInvokeOnMainThreadを呼び出すことはできません。c#単純な背景スレッド

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 

バックグラウンドスレッドが完了する前に消えることはメソッドが返すときことを停止しますか?

ありがとうございました。

答えて

2

:このような

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

ありがとうございます。タスク変数を削除することもできます。 yourClass.CreateItems(トークン).ContinueWith(t => { }、TaskScheduler.FromCurrentSynchronizationContext()); "TaskScheduler.FromCurrentSynchronizationContext()"が必要です。 は、私はクライアントのための最も単純なAPIをしたいので、そのいずれか: 1)yourClass.GetItemsAsync(デリゲート(項目[]項目) {\t //アイテム で何かを行います}); 又は2)yourClass.CreateItems(。)ContinueWith(T => { // t.Resultで何かをする}、TaskScheduler.FromCurrentSynchronizationContext())。 私はまだGetItemsAsyncメソッドが必要です。 – Bbx

+0

はい、そうです。しかし、GetItemsAsyncは、OOの原則やタスクベースのプログラミングに関して、従来のソリューションよりはるかに明確です。たとえば、提案された手法を使用すると、C#5.0の待機/非同期機能への移行がはるかに簡単になります。 OOPの面では、この解決策はホイールを発明しようとしていないため、より簡単です。 そして、テスト容易性を忘れないでください。この場合、模擬実装からこのタスクを簡単に取得して、クライアントコードのコーナーケースをテストできます。 –

1

コードは正常です。

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

items変数がラムダに取り込まれているので、これも問題ありません。

外部変数を使用するラムダを記述すると、C#コンパイラは変数を含むクラスを作成します。これはクロージャと呼ばれ、ラムダ内の変数にアクセスできることを意味します。私はそれがあまりにも多くをやっているので、あなたの方法は、わずかに、Single Responsibility Principleに違反していると思われる

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

速い返信ありがとう:)私は、データトラフィックが完全に無視されていることを知るために、iOS APIについての私の最後の質問の後に、ほとんど気にしていませんでした。私はそれが他のものではなく、いくつかのものには良いと思う。 – Bbx

1

はよりよいsoultionです。

私が代わりにGetItemsAsyncでそれを包むのTaskを返すようにCreateItemsを変更することをお勧めまず第一に:

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つだけの責任で、よりクリアなコードを取得します。 Taskクラス自体は、first class objectとして非同期操作を表すのに最適なツールです。提案された手法を使用すると、クライアントコードを変更することなく単体テストのための偽の動作で現在の実装を簡単に模擬することができます。

+0

ありがとう、それはより良いです:) – Bbx

+0

@Bbx:コードを変更する必要があるため、結果機能がありません:タスク= Task.Factory .... –

+0

ありがとう、ええ私は私のコメントを投稿した後、それを実現しました:) – Bbx

2

は何か:ここ

+0

ラブリー、ありがとう。しかし個人的に、私は@ laszlokiss88の答えを好む - それはまた2行(本質的に)であり、私にとってより単純に思える。 – Bbx