簡體   English   中英

如何在C#中將事件作為參數傳遞

[英]How to pass around event as parameter in C#

我正在為多線程應用程序編寫單元測試,我需要等到觸發特定事件后才能知道異步操作已完成。 例如,當我調用repository.add(something) ,我等待事件AfterChange進行任何聲明。 所以我寫了一個實用函數來做到這一點:

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));
}

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

反應性擴展通過兩種方式處理此問題:

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

     var x = Observable.FromEvent<EventArgs>(button, "Click"); 
  • 您可以提供一個用於訂閱的委托和一個用於取消訂閱的委托:

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

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

當然,如果您很高興自己使用反應性擴展,則可以使用這些方法並使測試使用IObservable<T> :)

根據Jon Skeet的答案中使用的語法,這可能是一個自定義解決方案:

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());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM