【问题标题】:How to pass an event to a method?如何将事件传递给方法?
【发布时间】:2011-02-03 08:28:44
【问题描述】:

我想创建一个将事件作为参数的方法,并将 eventHandler 添加到它以正确处理它。像这样:

我有两个事件:

public event EventHandler Click;
public event EventHandler Click2;

现在我想像这样(伪代码)将特定事件传递给我的方法:

public AttachToHandleEvent(EventHandler MyEvent)
{
    MyEvent += Item_Click;
}

private void Item_Click(object sender, EventArgs e)
{
    MessageBox.Show("lalala");
}

ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);

有可能吗?

我注意到这段代码运行良好,然后返回我的项目并注意到当我传递在我的类中声明的事件时,它可以工作,但是当我从其他类传递事件时它仍然不起作用。

我得到的是这个错误:

事件 'System.Windows.Forms.ToolStripItem.Click' 只能出现在左侧 += 或 -=

【问题讨论】:

  • @Petar Minchev 我不知道为什么......你是第一个帮助我的人。谢谢。
  • 我已经更新了问题以反映我正在处理的问题
  • @tomaszs 查看我的编辑。这是因为之前事件是在调用它的同一个类中声明的。

标签: c# events event-handling delegates


【解决方案1】:

使用面向对象的解决方案更新此问题。

您可以创建一个为您处理该事件的对象,而不是使用注册事件的Action<EventHandler>

    public class AEvent
    {
        private readonly A aInstance;
        private AEvent(A instance) {
            aInstance = instance;
        }        

        public void Add(EventHandler eventHandler)
            => a.Event1 += eventHandler;

        public void Remove(EventHandler eventHandler)
            => a.Event1 -= eventHandler;

        public EventHandler Invoke => aInstance.Event1;
    }

然后像这样使用该对象:

    static void Main(){
        A a = new A();
        AEvent aEvent = new AEvent(A)
        aEvent.Add(handler);
        a.Invoke();
    }

【讨论】:

    【解决方案2】:

    我像这样传递函数/方法(而不是事件):

    class A
    {
        public void something()
        {
            var myAction = 
                new Action<object, object>((sender, evArgs) => {
                    MessageBox.Show("hiii, event happens " + (evArgs as  as System.Timers.ElapsedEventArgs).SignalTime); 
                });
            B.timer(myAction);
        }
    }
    
    class B
    {
        public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
        {
            System.Timers.Timer myTimer = new System.Timers.Timer();
            myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
            myTimer.Interval = 2000;
            myTimer.Start();
        }
    }
    

    【讨论】:

      【解决方案3】:

      我的原始答案适用于定义事件的课程,但您已经更新了您的问题以反映您希望从定义课程之外完成此操作,所以我对此表示不满。

      只有定义事件的类才能引用事件使用的隐式委托变量。在该类之外,您只能通过+=-= 访问addremove 方法。这意味着你不能直接做你所要求的。但是,您可以使用函数式方法。

      class A{
          public event EventHandler Event1;
      
          public void TriggerEvent1(){
              if(Event1 != null)
                  Event1(this, EventArgs.Empty);
          }
      }
      
      class B{
          static void HandleEvent(object o, EventArgs e){
              Console.WriteLine("Woo-hoo!");
          }
      
          static void AttachToEvent(Action<EventHandler> attach){
              attach(HandleEvent);
          }
      
          static void Main(){
              A a = new A();
              AttachToEvent(handler=>a.Event1 += handler);
              a.TriggerEvent1();
          }
      }
      

      【讨论】:

      • 我已经检查了我的问题的更新版本,不幸的是它不起作用,你能帮我吗?
      【解决方案4】:

      您的问题表明您弄错了一些机制: 你不能传递事件!

      您很可能希望将函数作为参数传递,因此调用方法会在某个时候调用该其他方法。从技术上讲,这是一个代表。我建议使用已经定义好的 Action 类。这是一个示例 sn-p:

      void MyFunction (string otherArguments, Action onFinished){
          ...
          if (onFinished != null)
              onFinished.Invoke();
      }
      

      这样做的好处是,在调用 MyFunction 时,您可以使用内联语法声明 Action:

      MyFunction("my other argument", ()=>{
          ///do stuff here, which will be execuded when the action is invoked
      });
      

      【讨论】:

        【解决方案5】:

        我是这样做的:

        public AttachToHandleEvent(Object obj, string EventName)
        {
            EventInfo mfi = obj.GetType().GetEvent(EventName);
            MethodInfo mobj = mfi.GetAddMethod();
            mobj.Invoke(obj, new object[] { Item_Click});
        }
        
        private void Item_Click(object sender, EventArgs e)
        {
            MessageBox.Show("lalala");
        }
        
        ToolStripMenuItem tool = new ToolStripMenuItem();
        AttachToHandleEvent(tool "Click");
        

        谢谢大家的建议。没有您的帮助,这个解决方案无法完成。

        【讨论】:

        【解决方案6】:

        只要写tool.Click += Item_Click;

        编辑:来自 MSDN “事件只能从声明它们(它)的类或结构中调用”。所以你想要做的事情是不可能的。您能否详细说明您的需求?为什么要将事件作为参数传递?

        【讨论】:

        • 这不是我的缺点。你的答案是第一个也是正确的。我试过这个代码的更长版本,但它不想工作。我在这里写了这个问题来寻求帮助,但是当你回答后,我从这里重新检查了这个简单的例子,它恰好是你写的正确的。谢谢。
        • 还是有问题。很抱歉问错了问题,不是价格问题,而是关注我有错误时的情况
        • 我不能这样做,因为我想在 AttachToHandleEvent 中附加到 Click 事件
        • 这是对您原始答案的回应:“我没有看到问题,代码在语义上是正确的。编辑:您问是否可能。试试吧:)。它会采取与其在这里问,不如尝试一下。”
        • 我不会被一个看起来像傻傻的粉彩独角兽的人冒犯。
        【解决方案7】:
            delegate void doIt(object sender, object data);
            event doIt OnDoIt;
        
            void add(doIt theDel)
            {
                OnDoIt += theDel;
            }
        
            void doIt1(object a, object b)
            {
            }
        
            void doIt2(object a, object b)
            {
            }
        
            void add()
            {
                add(doIt1);
                add(doIt2);
            }
        

        【讨论】:

          【解决方案8】:

          这是不可能的。如果满足您的需要,您可以使用委托而不是事件。

          【讨论】: