Правильный способ вызова событий из C++ / CLI?
Мне было интересно, Как правильно вызывать события из C++ / CLI. В C# one sсначала сделайте копию обработчика, проверьте, не является ли он нулевым, а затем вызовите его. Существует ли аналогичная практика для C++ / CLI?
3 ответов
C++ / CLI позволяет переопределить raise
на пользовательское событие обработчики, поэтому вам не нужно тестировать для null
или копировать при вызове события. Конечно, внутри вашего пользовательского raise
вы все равно должны это сделать.
пример, адаптированный из MSDN для корректности:
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();
}
это не вся история! Обычно вам не нужно беспокоиться о обработчиках событий null в C++/CLI. Код для этих проверок генерируется для вас. Рассмотрим следующий тривиальный класс C++/CLI.
public ref class MyClass
{
public:
event System::EventHandler ^ MyEvent;
};
Если вы компилируете этот класс и разбираете его с помощью отражатель, вы получаете следующий код 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);
}
}
}
}
обычные проверки выполняются в методе raise. Если вы действительно не хотите нестандартного поведения, вы должны чувствовать себя комфортно объявление вашего события, как в приведенном выше классе, и его поднятие без страха перед обработчиком null.
Если ваша проблема в том, что поднять не является частным, а затем явно реализует его, как говорят документы:
http://msdn.microsoft.com/en-us/library/5f3csfsa.aspx
в итоге:
Если вы просто использовать событие ключевое слово, вы создаете "тривиальное" событие. Компилятор генерирует добавить/удалить/поднять и член делегата для вас. Созданный поднять функция (как говорят документы) проверяет наличие nullptr. Тривиальные события описаны здесь:
http://msdn.microsoft.com/en-us/library/4b612y2s.aspx
Если вы хотите "больше контроля", например, чтобы сделать поднять private, затем вы должны явно реализовать члены, как показано в ссылке. Необходимо явно объявить член данных для типа делегата. Затем вы используете событие ключевое слово для объявления членов, связанных с событием, как в примере 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
длинно, но это так.
-Рейли.