17 Stimmen

Richtige Art und Weise der Auslösung von Ereignissen aus C++/CLI?

Ich habe mich gefragt, was ist der richtige Weg, um Ereignisse von C++/CLI aus auszulösen. In C# ein s sollte zunächst eine Kopie des Handlers erstellen, prüfen, ob dieser nicht null ist, und ihn dann aufrufen . Gibt es eine ähnliche Praxis für C++/CLI?

32voto

IV. Punkte 1183

Das ist nicht die ganze Geschichte! Normalerweise müssen Sie sich in C++/CLI nicht um Null-Event-Handler kümmern. Der Code für diese Prüfungen wird für Sie generiert. Betrachten Sie die folgende triviale C++/CLI-Klasse.

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

Wenn Sie diese Klasse kompilieren und disassemblieren, indem Sie Reflektor erhalten Sie den folgenden 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);
            }
        }
    }
}

Die üblichen Prüfungen werden in der raise-Methode durchgeführt. Sofern Sie nicht wirklich ein benutzerdefiniertes Verhalten wünschen, sollten Sie Ihr Ereignis wie in der obigen Klasse deklarieren und es ohne Angst vor einem Null-Handler auslösen können.

19voto

Konrad Rudolph Punkte 503837

C++/CLI erlaubt es Ihnen, die raise en benutzerdefiniertes Ereignis Handler, so dass Sie nicht auf folgende Punkte testen müssen null oder kopieren, wenn das Ereignis ausgelöst wird. Natürlich können Sie innerhalb Ihrer benutzerdefinierten raise müssen Sie dies trotzdem tun.

Beispiel, der Korrektheit halber aus dem MSDN übernommen:

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

7voto

Reilly Punkte 796

Wenn Ihr Problem darin besteht, dass erhöhen nicht privat ist, dann implementieren Sie es explizit, wie es in der Dokumentation steht:

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

Zusammengefasst:

Wenn Sie nur die Veranstaltung erstellen Sie ein "triviales" Ereignis. Der Compiler erzeugt hinzufügen. / entfernen / erhöhen und das Delegiertenmitglied für Sie. Die generierte erhöhen Funktion (wie die Dokumentation sagt) prüft auf nullptr . Triviale Ereignisse werden hier dokumentiert:

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

Wenn Sie "mehr Kontrolle" wünschen, zum Beispiel um erhöhen private, dann müssen Sie die Mitglieder explizit implementieren, wie in dem Link gezeigt. Sie müssen explizit ein Datenelement für den Delegatentyp deklarieren. Dann verwenden Sie die Veranstaltung Schlüsselwort, um die ereignisbezogenen Mitglieder zu deklarieren, wie im Microsoft-Beispiel:

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

Wortreich, aber so ist es.

-Reilly.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X