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