Does C++/CLI event have any listeners?

2019-04-08 16:10发布

问题:

In C# I can check if an event has any listeners:

C# Example:

public static event EventHandler OnClick;

if (OnClick != null)
    OnClick(null, new EventArgs() );

In C++/CLI checking if the event is null is not necessary.

C++/CLI Example:

delegate void ClickDelegate( Object^ sender, MyEventArgs^ e );
event ClickDelegate^ OnClick;

OnClick (sender, args);

BUT, in the project I am working on, I don’t want to construct the MyEventArgs object if there are no listeners.

How do I find out if OnClick has any listeners in C++?

回答1:

It seems you can't check that with "trivial events", like you used, because you don't have direct access to the underlying field (as with auto-implemented properties in C#).

If you want to do this, you can specify the event's accessor methods and the backing field explicitly. See How to: Define Event Accessor Methods on how exactly to do that.



回答2:

Based on the comment discussion with @BenVoigt on @svick's original answer and the new MSDN article on C++/CLI events, I have created a minimal example of how to do this correctly. This code compiles and runs in a Visual Studio 2013 CLR project template targeting .NET 4.5. I haven't tested on other runtimes and targets, but it only uses basic .NET components.

TL;DR:

  • Make the backing field private
  • Lock each add, remove, and raise call with System::Threading::Monitor
  • Use the standard event handler convention:

    void MyEventHandler(Object ^sender, MyEventArgs ^e);
    
  • Use += and -= except when the backing field is a nullptr

My Solution:

// compile with: /clr
#include "stdafx.h"

using namespace System;
using System::Threading::Monitor;

public delegate void MyDelegate(Object ^sender, EventArgs ^e);

ref class EventSource {
private:
    MyDelegate ^myEvent;
    Object ^eventLock;

public:
    EventSource()
    {
        eventLock = gcnew Object();
    }

    event MyDelegate^ Event {
        void add(MyDelegate^ handler) {
            Monitor::Enter(eventLock);
            if (myEvent == nullptr)
            {
                myEvent = static_cast<MyDelegate^> (
                            Delegate::Combine(myEvent, handler));
            }
            else
            {
                myEvent += handler;
            }
            Monitor::Exit(eventLock);
        }

        void remove(MyDelegate^ handler) {
            Monitor::Enter(eventLock);
            if (myEvent != nullptr)
            {
                myEvent -= handler;
            }
            Monitor::Exit(eventLock);
        }

        void raise(Object ^sender, EventArgs ^e) {
            Monitor::Enter(eventLock);
            if (myEvent != nullptr)
                myEvent->Invoke(sender, e);
            Monitor::Exit(eventLock);
        }
    }

    void Raise()
    {
        Event(this, EventArgs::Empty);
    }
};

public ref struct EventReceiver {
    void Handler(Object ^sender, EventArgs ^e) {
        Console::WriteLine("In event handler");
    }
};

int main() {
    EventSource ^source = gcnew EventSource;
    EventReceiver ^receiver = gcnew EventReceiver;

    // hook event handler
    source->Event += gcnew MyDelegate(receiver, &EventReceiver::Handler);

    // raise event
    source->Raise();

    // unhook event handler
    source->Event -= gcnew MyDelegate(receiver, &EventReceiver::Handler);

    // raise event, but no handlers
    source->Raise();
}