订阅另一个事件的一次性事件火灾

时间:2012-12-10 22:34:01

标签: c# events

public event EventHandler ConstructDesign;
public DataGridView dataGrid = new DataGridView();
public FooClass(Action action) {
    ConstructDesign+=action;
    dataGrid.DataBindingComplete+=ConstructDesign;
}

public void Launch() {
    ConstructDesign(null, new EventArgs());
}

//IN A COMPLETELY DIFFERENT CLASS:
public void Main(string[] args) {
    var launcher = new FooClass(Fire);
    launcher.Launch();
}

public void Fire(object sender, EventHandlerArgs args...) {
    Console.WriteLine("Fired");
    //and after the first fire, action will be removed from the `ConstructDesign`.
}

所以基本上我在这里想要实现的是如何做到以下几点: 通过代码手动添加到Action的{​​{1}},在触发后,它会将自己从事件处理程序ConstructDesign中删除。任何想法?

5 个答案:

答案 0 :(得分:1)

我不相信你可以阻止事件发生,但你可以阻止事件中的代码执行。这很简单,在类级别添加static bool,将其初始化为true,在第一次执行后将其设置为false。将代码包装在if (firstExecution) {//actions I only want executed the first time the event fires}

中的事件处理程序中

答案 1 :(得分:1)

我不明白为什么你班上有活动,因为你没有在任何地方订阅它们。改为调用传递的操作:

Action _action;

public FooClass(Action action) 
{
    _action = action;
}

public void Launch() 
{
    if (_action == null)
        return;

     _action();
     _action = null;        
}

答案 2 :(得分:1)

我没有找到一个很好的方法来在第一次使用后取消订阅该事件。 (你当然可以使用反射密集的方法,但我怀疑如果重构改变了事件的名称,编译器会抱怨。)

这是一个仅使用委托的程序,因此编译器仍然可以很好地为您服务。它可能不像你需要的那么轻,但是因为我接受了我自己的启发的挑战,我想我会分享它。

MyEvent += SingleUseEventHandler<AssemblyLoadEventArgs, AssemblyLoadEventHandler>
   .Create(This_MyEventOccurred);

这里定义了魔法:

public class SingleUseEventHandler<TArgs,THandler>
  where TArgs : EventArgs
{
  public static THandler Create(EventHandler<TArgs> handler)
  {
     var helper = new SingleUseEventHandler<TArgs, THandler>(handler);
     EventHandler<TArgs> h = helper.InvokeIfFirstTime;
     return (THandler)(object)Delegate.CreateDelegate(typeof(THandler), h.Target, h.Method);
  }

  public void InvokeIfFirstTime(object sender, TArgs args)
  {
     if (!raised)
     {
        raised = true;
        handler(sender, args);
     }
  }

  public SingleUseEventHandler(EventHandler<TArgs> handler)
  {
     this.handler = handler;
  }

  bool raised;
  readonly EventHandler<TArgs> handler;
}

当然,C#不会推断出委托类型,因此你必须明确指定它。

如果事件的定义是EventHandler,您可以改为使用它:

MyEvent += SingleUseEventHandler<SomeEventArgs>.Create(SomeHandlerMethod);


public static class SingleUseEventHandler<TArgs>
  where TArgs : EventArgs
{
  public static EventHandler<TArgs> Create(EventHandler<TArgs> handler)
  {
     var helper = new SingleUseEventHandler<TArgs, EventHandler<TArgs>>(handler);
     return helper.InvokeIfFirstTime;
  }
}

这是一个示例程序:

class Program
{
  static event AssemblyLoadEventHandler MyEvent;
  static int callCount;
  static void Main(string[] args)
  {
     MyEvent += SingleUseEventHandler<AssemblyLoadEventArgs, AssemblyLoadEventHandler>
        .Create(Load);

     foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
     {
        Console.WriteLine("Raising event for " + assembly.GetName().Name);
        MyEvent(null, new AssemblyLoadEventArgs(assembly));
     }
  }

  static void Load(object sender, AssemblyLoadEventArgs eventArgs)
  {
     Console.WriteLine(++callCount);
  }
}

答案 3 :(得分:0)

只有拥有订阅者才会触发事件,所以你不能只通过-=删除代理,或者我误解了你想做什么?

答案 4 :(得分:0)

        public class FooEvents {
            public event EventHandler ConstructDesign;
            public DataGridView dataGrid = new DataGridView();
            public FooEvents(Action action) {
                ConstructDesign+=action;
                dataGrid.DataBindingComplete+=ConstructDesign;
                dataGrid.DataBindingComplete+=RemoveSubscribtion;
            }

            public void Launch() {
                ConstructDesign(this, new EventArgs()); //passes FooEvent and fires.
            }

            private void RemoveSubscribtion(object sender, EventArgs args) {
                 dataGrid.DataBindingComplete-=ConstructDesign;
                 dataGrid.DataBindingComplete-=RemoveSubscribtion;
            }

       public Main {
            public void Main(string[] args) {
                var launcher = new FooClass(Fire);
                launcher.Launch();
            }

            public void Fire(object sender, EventHandlerArgs args) {
                Console.WriteLine("Fired");
            }
       }