Proper way of raising events from C++/CLI?

北慕城南 提交于 2019-11-30 08:28:53

C++/CLI allows you to override raise in custom event handlers so you don't have to test for null or copy when raising the event. Of course, inside your custom raise you still have to do this.

Example, adapted from the MSDN for correctness:

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

This isn't the whole story! You don't usually have to worry about null event handlers in C++/CLI. The code for these checks is generated for you. Consider the following trivial C++/CLI class.

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

If you compile this class, and disassemble it using Reflector, you get the following c# code.

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

The usual checks are being done in the raise method. Unless you really want custom behavior, you should feel comfortable declaring your event as in the above class, and raising it without fear of a null handler.

If your issue is that raise isn't private, then explicitly implement it like the docs say:

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

In summary:

If you just use the event keyword, you create a "trivial" event. The compiler generates add/remove/raise and the delegate member for you. The generated raise function (as the docs say) checks for nullptr. Trivial events are documented here:

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

If you want "more control", for example to make raise private, then you have to explicitly implement the members as shown in the link. You must explicitly declare a data member for the delegate type. Then you use the event keyword to declare the event-related members, as in the Microsoft example:

// 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

Wordy, but there it is.

-reilly.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!