2010-05-18 26 views
2

我正在爲多線程應用程序編寫單元測試,其中需要等待特定事件觸發,以便我知道異步操作已完成。例如,當我打電話給repository.add(something)時,我在做任何斷言之前等待事件AfterChange。所以我寫了一個實用函數來做到這一點:如何在C#中傳遞事件作爲參數

public static void SyncAction(EventHandler event_, Action action_) 
{ 
    var signal = new object(); 
    EventHandler callback = null; 
    callback = new EventHandler((s, e) => 
    { 
     lock (signal) 
     { 
      Monitor.Pulse(signal); 
     } 

     event_ -= callback; 
    }); 

    event_ += callback; 

    lock (signal) 
    { 
     action_(); 
     Assert.IsTrue(Monitor.Wait(signal, 10000)); 
    } 
} 

但是,編譯器阻止將事件傳遞出類。有沒有辦法實現這一點?

下面是使用反射的解決方案。

public static void SyncAction(object target_, string event_, Action action_) 
{ 
    SyncAction(
     new List<Pair<object, string>>() { new Pair<object, string>(target_, event_) }, 
     action_); 
} 

public static void SyncAction(IEnumerable<Pair<object, string>> events_, Action action_) 
{ 
    var events = events_ 
     .Select(a => new Pair<object, EventInfo>(a.First, a.First.GetType().GetEvent(a.Second))) 
     .Where(a => a.Second != null); 

    var count = events.Count(); 
    var signal = new object(); 
    var callback = new EventHandler((s, e) => 
    { 
     lock (signal) 
     { 
      --count; 
      Monitor.Pulse(signal); 
     } 
    }); 

    events.ForEach(a => a.Second.AddEventHandler(a.First, callback)); 

    lock (signal) 
    { 
     action_(); 
     while (count > 0) 
     { 
      Assert.IsTrue(Monitor.Wait(signal, 10000)); 
     } 
    } 
    events.ForEach(a => a.Second.RemoveEventHandler(a.First, callback)); 
} 

回答

4

的問題是,事件是不是真的在.NET :(一流值(同上,屬性,方法等)

Reactive Extensions處理這兩種方式:

  • 你可以提供事件的名稱和目標,它會使用反射...類似這樣的:

    var x = Observable.FromEvent<EventArgs>(button, "Click"); 
    
  • 您可以退訂提供訂閱委託和委託:

    var x = Observable.FromEvent<EventArgs>(
         handler => button.Click += handler, 
         handler => button.Click -= handler); 
    

(確切的方法可能會略有不同,但這是一般的想法)

當然,如果你「再高興自己使用無功擴展,你可以使用這些方法,讓你的測試使用IObservable<T> :)

+0

上午第二次的做法,並會嘗試上的反射。 Rx尚未獲得我公司的批准,但我無法等待。但使用Rx編寫測試用例肯定會容易得多。 – 2010-05-18 14:20:11

3

基於在Jon Skeet's answer使用的語法,這可能是一個定製的解決方案:

public static void AssertEventIsFired<T>(
    Action<EventHandler<T>> attach, 
    Action<EventHandler<T>> detach, 
    Action fire) where T : EventArgs 
{ 
    AutoResetEvent fired = new AutoResetEvent(false); 
    EventHandler<T> callback = new EventHandler<T>(
     (s, e) => 
     { 
      detach(callback); 
      fired.Set(); 
     }); 

    attach(callback); 
    fire(); 
    Assert.IsTrue(fired.WaitOne(10000)); 
} 

使用方法:

AssertEventIsFired<EventArgs>(
    h => obj.EventFired += h, 
    h => obj.EventFired -= h, 
    () => obj.DoSomethingToFireEvent()); 
+0

我正在使用與現在完全相同的工作。這工作正常,雖然不優雅。但是當我用它來連接多個事件時,它變得很煩人,我需要2個附件和分離列表。 – 2010-05-18 14:15:26