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?
Antworten
Zu viele Anzeigen?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.
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();
}
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.