Modo corretto di raccogliere eventi da C ++ / CLI?
Domanda
Mi chiedevo quale fosse il modo corretto di raccogliere eventi da C ++ / CLI. In C # one s dovrebbe prima fare una copia del gestore, controllare se non è null e quindi chiamalo . Esiste una pratica simile per C ++ / CLI?
Soluzione
C ++ / CLI ti consente di sovrascrivere raise
in gestori di eventi personalizzati in modo da non dover testare null
o copiarli quando si solleva l'evento. Naturalmente, all'interno del tuo raise
personalizzato devi ancora farlo.
Esempio, adattato da MSDN per correttezza:
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();
}
Altri suggerimenti
Questa non è l'intera storia! Di solito non devi preoccuparti dei gestori di eventi null in C ++ / CLI. Il codice per questi controlli è generato per te. Considera la seguente banale classe C ++ / CLI.
public ref class MyClass
{
public:
event System::EventHandler ^ MyEvent;
};
Se compili questa classe e la smonti usando Reflector , ottieni il seguendo il codice 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);
}
}
}
}
I soliti controlli sono stati fatti nel metodo raise. A meno che tu non voglia davvero un comportamento personalizzato, dovresti sentirti a tuo agio nel dichiarare il tuo evento come nella classe sopra e nel sollevarlo senza paura di un gestore null.
Se il tuo problema è che raise non è privato, implementalo esplicitamente come dicono i documenti:
http://msdn.microsoft.com/en-us/library/5f3csfsa.aspx
In sintesi:
Se si utilizza semplicemente la parola chiave evento , si crea una " banale " evento. Il compilatore genera aggiungi /remove/raise e il membro delegato per te. La funzione raise generata (come dicono i documenti) controlla nullptr . Gli eventi di prova sono documentati qui:
http://msdn.microsoft.com/en-us/library/4b612y2s.aspx
Se vuoi "più controllo", ad esempio per rendere privato raise , devi implementare esplicitamente i membri come mostrato nel link. È necessario dichiarare esplicitamente un membro di dati per il tipo delegato. Quindi si utilizza la parola chiave evento per dichiarare i membri relativi all'evento, come nell'esempio 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
Wordy, ma eccolo qui
-reilly.