2011-12-05 7 views
14

どういうわけか、私はその問題に遭遇する最初の人だとは信じられません(そして、私はソリューションを直接見ることができないほど愚かな人だとは信じたくありません)。しかし、私の検索フーは強くありませんでした十分な。完了イベントを使用しているときスパゲッティコードを避けるには?

私は定期的に状況に遭遇します。私は、時間を掛けていくステップを順番に実行する必要があります。ワークフローは次のようになります

var data = DataGetter.GetData(); 
var processedData = DataProcessor.Process(data); 
var userDecision = DialogService.AskUserAbout(processedData); 
// ... 

各ステップでUIをブロックしたくないため、すべてのメソッドがすぐに戻り、終了したらイベントを発生させます。今はしゃぎが

DataGetter.Finished += (data) => 
    { 
     DataProcessor.Finished += (processedData) => 
     { 
      DialogService.Finished(userDecision) => 
       { 
        // .... 
       } 
       DialogService.AskUserAbout(processedData); 
      } 
     DataProcessor.Process(data); 
    }; 
DataGetter.GetData(); 

に上記のコードブロックが突然変異するので、すさまじいこれが私の好みのためContinuation-passing styleのようなあまりを読み込み、このコードを構造化するためのより良い方法がなければなりません。しかしどうですか?

+1

私は継続タスクの考えさせる:http://www.blackwasp.co.uk/ContinuationTasks.aspx –

+3

これは、 'await'とC#5で大量に容易になります。そう、ええ、人々はこれを常に踏襲しています。実際には、それを実際にコア言語にするには十分です。 –

答えて

7

正しい方法は、コンポーネントを同期方法で設計し、バックグラウンドスレッドで完全なチェーンを実行することです。

+0

入力いただきありがとうございます!これは、ユーザーインタラクションによって少し難しくなりました。これは、UI以外のスレッド(少なくともWPF)では簡単にはできません。しかし、確実にスパゲッティのレベルをそれほど減らすことはできません。 – Jens

+0

私たちはどんな種類のユーザー入力をしていますか? –

+0

タスクを実行する(マシンを操作する)プロンプトが表示され、終了したら "次へ"を押すか、またはprocessedDataで何をするかを尋ねられます。 – Jens

2

すべてをBackgroundWorkerに入れることができます。次のコードは、GetData、Process、およびAskUserAboutメソッドを同期的に実行するように変更した場合にのみ正しく動作します。このような

何か:

private BackgroundWorker m_worker; 

private void StartWorking() 
{ 
    if (m_worker != null) 
     throw new InvalidOperationException("The worker is already doing something"); 

    m_worker = new BackgroundWorker(); 
    m_worker.CanRaiseEvents = true; 
    m_worker.WorkerReportsProgress = true; 

    m_worker.ProgressChanged += worker_ProgressChanged; 
    m_worker.DoWork += worker_Work; 
    m_worker.RunWorkerCompleted += worker_Completed; 
} 

private void worker_Work(object sender, DoWorkEventArgs args) 
{ 
    m_worker.ReportProgress(0, "Getting the data..."); 
    var data = DataGetter.GetData(); 

    m_worker.ReportProgress(33, "Processing the data..."); 
    var processedData = DataProcessor.Process(data); 

    // if this interacts with the GUI, this should be run in the GUI thread. 
    // use InvokeRequired/BeginInvoke, or change so this question is asked 
    // in the Completed handler. it's safe to interact with the GUI there, 
    // and in the ProgressChanged handler. 
    m_worker.ReportProgress(67, "Waiting for user decision..."); 
    var userDecision = DialogService.AskUserAbout(processedData); 

    m_worker.ReportProgress(100, "Finished."); 
    args.Result = userDecision; 
} 

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs args) 
{ 
    // this gets passed down from the m_worker.ReportProgress() call 
    int percent = args.ProgressPercentage; 
    string progressMessage = (string)args.UserState; 

    // show the progress somewhere. you can interact with the GUI safely here. 
} 

private void worker_Completed(object sender, RunWorkerCompletedEventArgs args) 
{ 
    if (args.Error != null) 
    { 
     // handle the error 
    } 
    else if (args.Cancelled) 
    { 
     // handle the cancellation 
    } 
    else 
    { 
     // the work is finished! the result is in args.Result 
    } 
} 
4

Task Parallel Libraryは、そのようなコードのために有用であることができます。 TaskScheduler.FromCurrentSynchronizationContext()を使用して、UIスレッドでタスクを実行できます。

Task<Data>.Factory.StartNew(() => GetData()) 
      .ContinueWith(t => Process(t.Result)) 
      .ContinueWith(t => AskUserAbout(t.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
関連する問題