2013-01-16 44 views
2

假設我有三個產品在列表中。爲了實現某種行動,所有這三者都需要具有某種特定類型。爲了找出產品的類型,我需要撥打服務電話並等待迴應。使用RX同步多個事件

我想要做的就是等待所有三個響應(也許在發生錯誤時使用超時),並且收集所有信息時,決定是否啓用可能的操作。

我曾經通過一些計數器或重置事件來跟蹤已完成的事件來解決此問題,但我想看看是否可以使用Rx以更清晰的方式執行此操作。

由於我不太熟悉Rx,我正在尋找一些提示/指針。我明白我可以使用

Observable.FromEventPattern 

因爲我等待的事件。我訂閱並等待迴應並處理它。我只是不清楚如何組合多個事件。

回答

4

的Combinator的您正在尋找的是CombineLatest

假設你有一個這樣的類:

public class Foo 
{ 
    public delegate void FooEventHandler(object sender, EventArgs args); 

    public event FooEventHandler FirstEvent = delegate {};  
    public event FooEventHandler SecondEvent = delegate {};  
    public event FooEventHandler ThirdEvent = delegate {};  

    public void DoIt() 
    { 
     FireOne(); 
     FireTwo(); 
     FireThree(); 
    } 

    public void FireOne() 
    { 
     Console.WriteLine("Firing event 1..."); 
     Thread.Sleep(1000); 
     FirstEvent(this, new EventArgs()); 
    } 
    public void FireTwo() 
    { 
     Console.WriteLine("Firing event 2..."); 
     Thread.Sleep(1000); 
     SecondEvent(this, new EventArgs()); 
    } 
    public void FireThree() 
    { 
     Console.WriteLine("Firing event 3..."); 
     Thread.Sleep(1000); 
     ThirdEvent(this, new EventArgs()); 
    } 
} 

首先你要「轉換」這些事件來Observable

var foo = new Foo(); 
var firstWatcher = Observable.FromEventPattern(foo, "FirstEvent"); 
var secondWatcher = Observable.FromEventPattern(foo, "SecondEvent"); 
var thirdWatcher = Observable.FromEventPattern(foo, "ThirdEvent"); 

現在你會想要「所有這些都已經發射的時候只有發射」選擇器,它是CombineLatest

var allDone = Observable.CombineLatest(firstWatcher, secondWatcher, thirdWatcher); 

並對其進行測試:

using(allDone.Subscribe(_ => Console.WriteLine("Boop! You sunk my battleship!"))) 
{ 
    foo.DoIt(); 
}  

另類 「測試工具」:

var foo = new Foo(); 
var firstWatcher = Observable.FromEventPattern(foo, "FirstEvent"); 
var secondWatcher = Observable.FromEventPattern(foo, "SecondEvent"); 
var thirdWatcher = Observable.FromEventPattern(foo, "ThirdEvent"); 

var allDone = Observable.CombineLatest(firstWatcher, secondWatcher, thirdWatcher); 

// keep a handle on the subscription    
IDisposable subscription = null; 

// to prevent premature exiting... 
var blocker = new ManualResetEvent(false); 

// explicit subscribe 
subscription = allDone.Subscribe(
    whoCares => 
    { 
     Console.WriteLine("BOOM! We're done!"); 
     // always clean up after yourself 
     if(subscription != null) 
     { 
      subscription.Dispose(); 
     } 
     // it's ok, we can quit now 
     blocker.Set(); 
    }); 

foo.DoIt(); 

// Wait until it's clear to go ahead... 
blocker.WaitOne(); 
+0

感謝您的幫助JerKimball。很有用。如果事件是異步的,例如如果我們調用FireOne | Two | Three並且在進行一些異步調用而不觸發事件後立即返回這些事件?我試着用我的事件,它看起來像使用(allDone ...)將在事件結束前退出,因此allDone處理程序不會被調用(除非我做錯了什麼)。 – Flack

+0

例如,如果public void FireOne()包含int msDelay = 8000,那麼您是否有如何使您當前的示例工作的示例? 任務任務= Task.Factory.StartNew(()=> Thread.sleep代碼(msDelay)) .ContinueWith((任務起動)=> { Console.WriteLine( 「EVENT 1」); FirstEvent(這一點,新EventArgs()); },TaskContinuationOptions.LongRunning);' – Flack

+0

實際發生的情況是,在我蹩腳的小例子-harness中,程序在事件觸發前「結束」 - 更重要的是,創建的訂閱'allDone.Subscribe'正在處理中。在這個'using'的大括號之前放置一個'Console.ReadLine','Thread.Sleep(10000)'或任何你想要的東西(或者存儲它自己創建和處理的'IDisposable') – JerKimball