从 C++/CLI 引发事件的正确方法?

发布于 2024-07-12 00:43:01 字数 86 浏览 10 评论 0原文

我想知道从 C++/CLI 引发事件的正确方法是什么。 在 C# 中,首先应复制处理程序,检查它是否不为空,然后调用它。 C++/CLI 有类似的做法吗?

I was wondering what's the proper way of raising events from C++/CLI. In C# one should first make a copy of the handler, check if it's not null, and then call it. Is there a similar practice for C++/CLI?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

热情消退 2024-07-19 00:43:01

这不是故事的全部! 您通常不必担心 C++/CLI 中的空事件处理程序。 这些检查的代码是为您生成的。 考虑以下简单的 C++/CLI 类。

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

如果您编译此类,并使用 Reflector 反汇编它,您将获得以下 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 方法中完成的。 除非您确实想要自定义行为,否则您应该像在上面的类中一样轻松地声明您的事件,并引发它而不必担心空处理程序。

This isn't the whole story! You don't usually have to worry about null event handlers in C++/CLI. The code for these checks is generated for you. Consider the following trivial C++/CLI class.

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

If you compile this class, and disassemble it using Reflector, you get the following 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);
            }
        }
    }
}

The usual checks are being done in the raise method. Unless you really want custom behavior, you should feel comfortable declaring your event as in the above class, and raising it without fear of a null handler.

迟月 2024-07-19 00:43:01

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

C++/CLI allows you to override raise in custom event handlers so you don't have to test for null or copy when raising the event. Of course, inside your custom raise you still have to do this.

Example, adapted from the MSDN for correctness:

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();
}
谜兔 2024-07-19 00:43:01

如果您的问题是 raise 不是私有的,请按照文档所述显式实现它:

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

总之:

如果您只是使用事件 关键字,您创建了一个“琐碎”事件。 编译器生成add/remove/raise以及您的代表成员。 生成的 raise 函数(如文档所述)检查 nullptr。 此处记录了一些琐碎事件:

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

如果您想要“更多控制”,例如将 raise 设为私有,那么您必须显式实现链接中所示的成员。 您必须显式声明委托类型的数据成员。 然后,您使用 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

Wordy,但确实如此。

-赖利。

If your issue is that raise isn't private, then explicitly implement it like the docs say:

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

In summary:

If you just use the event keyword, you create a "trivial" event. The compiler generates add/remove/raise and the delegate member for you. The generated raise function (as the docs say) checks for nullptr. Trivial events are documented here:

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

If you want "more control", for example to make raise private, then you have to explicitly implement the members as shown in the link. You must explicitly declare a data member for the delegate type. Then you use the event keyword to declare the event-related members, as in the Microsoft example:

// 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, but there it is.

-reilly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文