2011-12-05 8 views
14

Her nasılsa, bu soruna koşan ilk kişi olduğuma inanamıyorum (ve doğrudan bir çözümü görmemek için tek aptal olduğuma inanmak istemiyorum), ama arama-fu'm güçlü değildi yeterli.Tamamlama olaylarını kullanırken spagetti kodundan nasıl korunulur?

Düzenli olarak bir durumla karşılaşıyorum, birbiri ardına birkaç zaman harcayan adımlar atmam gerektiğinde. iş akışı Her adım sırasında UI engellemek istemiyoruz

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

gibi yani her yöntem derhal geri gelmez, görünüyor ve Bittikten sonra bir olay yükseltir. Şimdi neşe

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

Bu çok fazla benim zevkime Continuation-passing style gibi okur ve bu kodu yapısı için daha iyi bir yolu olmalı içine yukarıdaki kod bloğu mutasyon beri, ortaya çıkar. Ama nasıl?

+1

bana Devam Görevleri düşündürüyor: http://www.blackwasp.co.uk/ContinuationTasks.aspx –

+3

Bu 'await' ile C# 5'de kitlesel kolay olacaktır. Evet, insanlar her zaman bu işe koşarlar ve bu, aslında onu çekirdek dil haline getirmek için yeterince iyi değildir. –

cevap

7

Bileşenlerinizi eşzamanlı olarak tasarlamanın ve zincirin arka plan iş parçacığı içinde yürütülmesinin doğru yolu.

+0

Giriş için teşekkürler! Bu, kullanıcı etkileşimi ile biraz daha zor hale getirilir; bu, UI olmayan bir iş parçacığı üzerinde kolayca gerçekleştirilemez (en azından WPF'de). Ama kesinlikle bu şekilde daha az spagetti seviyesi elde edebiliriz. – Jens

+0

Ne tür bir kullanıcı girişi hakkında konuşuyoruz? –

+0

Kullanıcı sizden bir işlemi (makine çalıştırarak) ve işiniz bittiğinde "İleri" ye basmanız ya da işlenen Veri'de ne yapılması gerektiği sorulduğunda sizden istenebilir. – Jens

2

Her şeyi bir BackgroundWorker'a yerleştirebilirsiniz. Aşağıdaki kod, yalnızca GetData, Process ve AskUserAbout yöntemlerini senkronize olarak çalıştırmak için değiştirirseniz düzgün çalışır. Böyle

şey:

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 böyle kod için yararlı olabilir. TaskScheduler.FromCurrentSynchronizationContext() öğesinin UI iş parçacığı üzerinde çalıştırmak için kullanılabileceğini unutmayın.

Task<Data>.Factory.StartNew(() => GetData()) 
      .ContinueWith(t => Process(t.Result)) 
      .ContinueWith(t => AskUserAbout(t.Result), TaskScheduler.FromCurrentSynchronizationContext());