繁体   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