【问题标题】:Proper way of raising events from C++/CLI?从 C++/CLI 引发事件的正确方法?
【发布时间】:2010-10-02 13:05:13
【问题描述】:

我想知道从 C++/CLI 引发事件的正确方法是什么。在 C# 中一个 should first make a copy of the handler, check if it's not null, and then call it。 C++/CLI 有类似的做法吗?

【问题讨论】:

    标签: events c++-cli raise


    【解决方案1】:

    C++/CLI 允许您在 custom event 处理程序中覆盖 raise,因此您不必在引发事件时测试 null 或复制。当然,在您的自定义 raise 中,您仍然必须这样做。

    示例,改编自 MSDN 以确保正确性:

    public delegate void f(int);
    
    public ref struct E {
       f ^ _E;
    public:
       void handler(int i) {
          System::Console::WriteLine(i);
       }
    
       E() {
          _E = nullptr;
       }
    
       event f^ Event {
          void add(f ^ d) {
             _E += d;
          }
          void remove(f ^ d) {
            _E -= d;
          }
          void raise(int i) {
             f^ tmp = _E;
             if (tmp) {
                tmp->Invoke(i);
             }
          }
       }
    
       static void Go() {
          E^ pE = gcnew E;
          pE->Event += gcnew f(pE, &E::handler);
          pE->Event(17);
       }
    };
    
    int main() {
       E::Go();
    }
    

    【讨论】:

      【解决方案2】:

      这不是故事的全部!您通常不必担心 C++/CLI 中的空事件处理程序。这些检查的代码是为您生成的。考虑以下简单的 C++/CLI 类。

      public ref class MyClass
      {
      public:
          event System::EventHandler ^ MyEvent;
      };
      

      如果你编译这个类,然后用Reflector反汇编它,你会得到下面的c#代码。

      public class MyClass
      {
          // Fields
          private EventHandler <backing_store>MyEvent;
      
          // Events
          public event EventHandler MyEvent
          {
              [MethodImpl(MethodImplOptions.Synchronized)] add
              {
                  this.<backing_store>MyEvent = (EventHandler) Delegate.Combine(this.<backing_store>MyEvent, value);
              }
              [MethodImpl(MethodImplOptions.Synchronized)] remove
              {
                  this.<backing_store>MyEvent = (EventHandler) Delegate.Remove(this.<backing_store>MyEvent, value);
              }
              raise
              {
                  EventHandler <tmp> = null;
                  <tmp> = this.<backing_store>MyEvent;
                  if (<tmp> != null)
                  {
                      <tmp>(value0, value1);
                  }
              }
          }
      }
      

      通常的检查是在 raise 方法中进行的。除非您真的想要自定义行为,否则您应该可以轻松地在上述类中声明您的事件,并在不担心 null 处理程序的情况下引发它。

      【讨论】:

      • 我对这种方法的问题是“raise”方法不是私有的(就像在 C# 中一样)并且显示在智能感知中。
      • @Filip:所以请使用自定义事件并将private: 放在前面。
      【解决方案3】:

      如果您的问题是 raise 不是私有的,那么就像文档说的那样明确地实现它:

      http://msdn.microsoft.com/en-us/library/5f3csfsa.aspx

      总结:

      如果您只使用 event 关键字,您将创建一个“微不足道”的事件。编译器生成add/remove/raise和您的代表成员。生成的 raise 函数(如文档所说)检查 nullptr。此处记录了琐碎的事件:

      http://msdn.microsoft.com/en-us/library/4b612y2s.aspx

      如果您想要“更多控制”,例如将 raise 设为私有,那么您必须显式实现链接中显示的成员。您必须显式声明委托类型的数据成员。然后您使用 event 关键字来声明与事件相关的成员,如 Microsoft 示例中所示:

      // event keyword introduces the scope wherein I'm defining the required methods
      // "f" is my delegate type
      // "Event" is the unrealistic name of the event itself
      event f^ Event
      {
            // add is public (because the event block is public)
            // "_E" is the private delegate data member of type "f"
            void add(f ^ d) { _E += d; }
      
         // making remove private
         private:
            void remove(f ^ d) { _E -= d; }
      
         // making raise protected
         protected:
            void raise(int i)
            { 
               // check for nullptr
               if (_E)
               {
                  _E->Invoke(i);
               }
            }
      }// end event block
      

      啰嗦,但就是这样。

      -赖利。

      【讨论】:

        猜你喜欢
        • 2018-10-12
        • 2012-07-11
        • 2010-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-06
        • 2012-05-25
        • 1970-01-01
        相关资源
        最近更新 更多