2014-02-11 30 views
5

我有以下代碼(爲發佈目的而簡化)。將基於事件的代碼轉換爲Rx

public class SomeDataObject 
{ 
    public delegate void ReadyEventHandler; 
    public delegate void ErrorEventHandler; 

    public event ReadyEventHandler Ready; 
    public event ErrorEventHandler Error; 
    ... 
} 

pubic class ConsumerClass 
{ 
    private SomeDataObject dataObject; 

    private Task<List<string>> GetStrings() 
    { 
     List<string> results = new List<string>(); 
     var tcs = new TaskCompletionSource<List<string>>(); 

     SomeDataObject.ReadyEventHandler ReadyHandler = null; 
     SomeDataObject.ErrorEventHandler ErrorHandler = null; 

     ReadyHandler +=() => 
     { 
      for (int i =0; i < dataObject.ItemCount; i++) 
       results.Add(dataObject[i].ToString()); 

      tcs.TrySetResult(results); 
     } 

     ErrorHandler +=() 
     { 
      tcs.TrySetException(new Exception("oops!"); 
     } 

     dataObject.Ready += ReadyHandler; 
     dataObject.Error += ErrorHandler; 

     dataObject.DoRequest(); 
    } 
} 

的想法是,當DoRequest調用時,SomeDataObject會得到一些數據,提高或者就緒或錯誤事件(詳細信息並不重要!)。如果數據可用,則ItemCount指示有多少項目可用。

我是Rx的新手,找不到任何可比的例子。那麼是否有可能將此轉換爲Rx,以便使用Observable.Create以某種方式返回IObservable<string>而不是Task<List<string>>

問候 艾倫

+0

你能告訴我們更多關於SomeDataObject API嗎?這似乎有點奇怪。每個實例多次調用DoRequest()是否有效?如果是,可以在現有請求正在運行時撥打電話嗎?它是否設計爲具有單個調用,但與多個消費者共享結果?感覺DoRequest應該返回一個可用於消費結果的句柄(可能是任務或IObservable )。如果出現錯誤,您是否希望能夠再次調用DoRequest()? –

+0

所有這一切都很重要,因爲一旦Observable流發生異常,它不會引發更多的事件 - 所以它可能更好地表示TResult中的錯誤,而不是吹掉可觀察或任務等。 –

+0

James,DoRequest只能被調用一旦。它保持內部狀態。它實際上是我無法控制的外部COM庫的一部分。 –

回答

6

馬修的答案很接近但有一些問題。首先,它是急切的,這通常不符合Rx /函數式編程的精神。接下來我想你會希望能夠在消費者處置時釋放事件句柄。最後,一個主題的使用應該是一個代碼氣味,這種情況下它指向上述兩個問題:-)

這裏我使用Observable.Create(它應該是工具箱中的#1 goto工具,與科目是你的最後手段)懶惰地連接,並且當訂閱被處置時也提供斷開/釋放事件。

private IObservable<string> GetStrings() 
{ 
    return Observable.Create<string>(o=> 
    { 
     SomeDataObject.ReadyEventHandler ReadyHandler = null; 
     SomeDataObject.ErrorEventHandler ErrorHandler = null; 

     ReadyHandler +=() => 
     { 
      for (int i =0; i < dataObject.ItemCount; i++) 
       o.OnNext(dataObject[i].ToString()); 

      o.OnCompleted(); 
     } 

     ErrorHandler +=() => 
     { 
      o.OnError(new Exception("oops!")); 
     } 

     dataObject.Ready += ReadyHandler; 
     dataObject.Error += ErrorHandler; 

     dataObject.DoRequest(); 

     return Disposable.Create(()=> 
      { 
       dataObject.Ready -= ReadyHandler; 
       dataObject.Error -= ErrorHandler; 
      }); 
    } 
} 

我也會考慮將dataObject移動到方法的參數。在Async系統中共享狀態是問題的根源。

+0

李,這正是我正在尋找的。我幾乎擁有它,但不能理解這個概念。 dataObject及其事件實際上是我無法控制的COM庫的一部分。我正在尋找處理事件的更好方式,而不是任務。 –

+0

你的解決方案的工作很好,但我現在質疑我是否應該將我的代碼轉換爲此。使用任務我可以讓呼叫者等待完整的結果 - 我如何使用Rx來做到這一點。 –

+0

查看我的回答,看看我調整了Lee的回答,以顯示如何獲得'List '回來並阻止以'Observable.Wait()'得到結果。 –

0

我認爲下面的代碼會做你想要什麼。 ReplaySubject用於確保調用者獲得所有結果,即使SomeDataObject事件立即開始。

private IObservable<string> GetStrings() 
{ 
    ReplaySubject<string> results = new ReplaySubject<string>(); 

    SomeDataObject.ReadyEventHandler ReadyHandler = null; 
    SomeDataObject.ErrorEventHandler ErrorHandler = null; 

    ReadyHandler +=() => 
    { 
     for (int i =0; i < dataObject.ItemCount; i++) 
      results.OnNext(dataObject[i].ToString()); 

     results.OnCompleted(); 
    } 

    ErrorHandler +=() 
    { 
     results.OnError(new Exception("oops!")); 
    } 

    dataObject.Ready += ReadyHandler; 
    dataObject.Error += ErrorHandler; 

    dataObject.DoRequest(); 

    return results; 
} 
1

在回答關於李的(完全可愛和蜱值得)回答您的意見,這裏是如何修改他的回答得到一個單一List<string>響應和塊:

private IObservable<List<string>> GetStrings(SomeDataObject dataObject) 
{ 
    return Observable.Create<List<string>>(o=> 
    { 
     SomeDataObject.ReadyEventHandler ReadyHandler = null; 
     SomeDataObject.ErrorEventHandler ErrorHandler = null; 

     ReadyHandler =() => 
     { 
      var results = new List<string>(dataObject.ItemCount); 
      for (int i =0; i < dataObject.ItemCount; i++) 
       results.Add(dataObject[i].ToString()); 

      o.OnNext(results); 
      o.OnCompleted(); 
     }; 

     ErrorHandler =() => 
     { 
      o.OnError(new Exception("oops!")); 
     }; 

     dataObject.Ready += ReadyHandler; 
     dataObject.Error += ErrorHandler; 

     dataObject.DoRequest(); 

     return Disposable.Create(()=> 
      { 
       dataObject.Ready -= ReadyHandler; 
       dataObject.Error -= ErrorHandler; 
      }); 
    }); 
} 

現在你可以阻斷這跟:

var results = GetStrings().Wait(); 

如果使用.NET 4.5,然後在async方法,你也可以做:

var results = await GetStrings(); 
+0

擁抱Rx James讓那可怕的異步/等待和損壞的.NET任務的東西死亡它需要的死亡;-) –

+0

我拒絕上升到這種公然誘餌先生! :) –