将变量捕获到事件处理程序中

发布于 2024-10-06 13:42:28 字数 1433 浏览 3 评论 0原文

我可能有点想太多了,但我可以使用一些帮助来确定执行以下操作的方法/最佳方法。

我有一个事件处理程序,它附加到一个对象,该对象是另一个类的属性。在我的事件处理程序中,我需要有关引发事件的对象的附加元数据(即包含该事件的对象的 ID)。从发送者和事件信息中无法获取我需要的信息。我的倾向是,这将是使用捕获变量的好地方,但我不确定我的实现想法。

因此,为了在代码中说明,我有一个事件处理程序:(

void MyEventHandler(object sender, EventArgs e){
    //Do Stuff here
}

请注意,我在这里使用基本 EventArgs,但在我的实际实现中,它们是一个专门的子类,并且该事件是使用通用 EventHandler 声明的)

我目前将其附加如下:

topObject.SubObject.EventToHandle += MyEventHandler;

我后来像这样分离它:

topObject.SubObject.EventToHandle -= MyEventHandler;

我在处理事件时需要 topObject 的 ID,所以我打算将 MyEventHandler 更改为具有以下签名:

void MyEventHandler(int id, object sender, EventArgs e)

并像这样附加事件处理程序:

topObject.SubObject.EventToHandle += (s,e) => MyEventHandler(topObject.ID, s,e);

我对此的关注有两个方面。

  1. 一旦我处于附加的函数之外,处理程序实际上会消失而不删除,范围是否存在问题。我过去见过一些奇怪的错误,当我使用 lambda 表达式时,事件处理程序在我身上消失了。并不是所有时候都是如此,只是在某些情况下如此。谁能告诉我这些情况可能是什么,这样我就知道什么时候可以安全地使用我所拥有的语法。
  2. 我记不清了,但我认为如果使用此语法,我永远无法删除事件处理程序,因为创建的隐式对象不相同。

由于这两个问题,我的想法是创建一个操作并保存该操作并使用它,直到我需要删除事件处理程序为止。我执行了以下操作:

Action<object, EventArgs> handler = (s,e) => MyEventHandler(topObject.ID, s,e);
topObject.SubObject.EventToHandle += handler;

我发现该操作无法转换为事件处理程序。是否有一些简单的方法可以进行此转换,同时仍然确保我可以分离事件处理程序?我是不是想太多了/有没有一种我现在没有看到的方法可以做到这一点?

I might be overthinking this one a little but I could use some help in identifying a way/the best way to do the following.

I have an event handler that is attached to an object that is a property of another class. In my event handler I need additional meta-data about the object that caused the event (i.e. the ID of the object it is contained in). From the sender and the event information there is no way to obtain the information I need. My inclination was that this would be a good place to use a captured variable but I'm unsure of my implementation ideas.

So to illustrate in code I have an event handler:

void MyEventHandler(object sender, EventArgs e){
    //Do Stuff here
}

(As a note I'm using base EventArgs here but in my actual implementation the are a specialized subclass and the event is declared using the generic EventHandler)

I am currently attaching it like this:

topObject.SubObject.EventToHandle += MyEventHandler;

I later detatch it like so:

topObject.SubObject.EventToHandle -= MyEventHandler;

I want topObject's ID when I'm handling the event so I was going to change the MyEventHandler to have the following signature:

void MyEventHandler(int id, object sender, EventArgs e)

and attach the event handler like this:

topObject.SubObject.EventToHandle += (s,e) => MyEventHandler(topObject.ID, s,e);

My concern with that is two fold.

  1. Is there a problem with the scope where the handler will actually disapear without removal once I'm outside the function where this is attached. I've seen some wierd errors in the past where event handler disapeared on me when I used lambda expression. Its not all the time, just in some cases. Can anyone enlighten me on what those cases might be so I know when its safe to use the syntax I had.
  2. I can't remember exactly but I don't think I can ever have the event handler removed if I use this syntax because the implicit object that is created is not the same.

Because of those two concerns my thought was to create an Action and save the action and use it until I needed to remove the event handler. I did the following:

Action<object, EventArgs> handler = (s,e) => MyEventHandler(topObject.ID, s,e);
topObject.SubObject.EventToHandle += handler;

I get that the action cannot be casted to an event handler. Is there some easy way that I can make this transformation that will still ensure that I can detach the event handler? Am i just over thinking this/ is there a way I'm not seeing right now to do this?

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

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

发布评论

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

评论(4

青衫负雪 2024-10-13 13:42:28

您可以创建所有漂亮的包装函数来包装现有的事件处理程序并为它们提供对象 ID,但您仍然必须显式存储结果委托以取消订阅事件。

我认为在没有丑陋的包装器的情况下做到这一点的唯一好方法是使用反应式扩展。它本质上允许您将事件转换为 IObservable,然后您可以将任何运算符应用于生成的 IObservable(例如 Select 将在您的情况下完成这项工作)。但它仍然没有那么优雅。

You can create all nice wrapper functions that wrap existing event-handlers and supply them with object ids, but you will still have to store the resulting delegate explicitly to unsubscribe the event.

The only nice way I see to do it without ugly wrappers is using reactive extensions. It essentially allows you to transform your event to an IObservable, and then you can apply any operator to the resulting IObservable (for example Select will do the job in your case). But it's still no that elegant.

花海 2024-10-13 13:42:28

引发事件的类中的事件处理程序签名应该是:

protected void OnMyEvent(object sender, EventArgs  e)
{
    ....
}

或者

protected void OnMyEvent(object sender, MyEventArgs  e)
{
    ....
}

在这种情况下,调用者将执行这种代码:

topObject.SubObject.MyEvent -= OnSubObjectMyEvent;

并像这样实现 OnSubObjectMyEvent (示例):

private void OnSubObjectMyEvent(object sender, MyEventArgs e)
{
   int topObjectId = ((SubObjectType)sender).TopObject.Id;
   ...
}

这里我假设 SubObject 有一个 TopObject 属性,它允许我获取顶级对象的 id。

这就是大多数 .NET Framework 类的工作方式。这种做法有什么问题吗?

An event handler signature in the class raising the event should be:

protected void OnMyEvent(object sender, EventArgs  e)
{
    ....
}

or

protected void OnMyEvent(object sender, MyEventArgs  e)
{
    ....
}

In this case, the caller would do this kind of code:

topObject.SubObject.MyEvent -= OnSubObjectMyEvent;

and implement OnSubObjectMyEvent like this (example):

private void OnSubObjectMyEvent(object sender, MyEventArgs e)
{
   int topObjectId = ((SubObjectType)sender).TopObject.Id;
   ...
}

Here I suppose SubObject has a TopObject property that allows me to get the top object's id.

That's the way most .NET Framework classes work. Is there anything wrong with this approach?

染火枫林 2024-10-13 13:42:28

如果事件处理程序需要顶部对象的 Id,我猜它不会破坏设计中的某些抽象层以使它们相互了解。

换句话说,您的事件处理程序可以如下所示:

void Handler(object sender, EventArgs e)
{
     var s = (SubObject) sender;
     int id = s.TopObject.ID;

     // do something with id...
}

我将按照发送者和参数的约定保留事件签名。

If the event handler needs the top object's Id, I would guess it would not break some abstraction layer in your design to make them aware of eachother.

In other words, your event handler can look like this:

void Handler(object sender, EventArgs e)
{
     var s = (SubObject) sender;
     int id = s.TopObject.ID;

     // do something with id...
}

I would keep event signatures in the convention of sender and args.

じ违心 2024-10-13 13:42:28

不要更改事件的签名。虽然从技术上讲,CLR 允许事件的任何签名,但整个框架设计为具有签名 (object sender, EventArgs args) 的事件是有原因的。事实上,对于违反此签名的事件有一个 FxCop 规则,CA1009:声明事件正确处理程序

事件处理程序方法需要两个
参数。第一个是类型
System.Object 并命名为“sender”。
这是提出的对象
事件。第二个参数的类型
System.EventArgs 并命名为“e”。

有多种解决方案(替代方案):

  • 将 topObject.ID 作为自定义 EventArgs 的成员传递。
  • 创建一个封装 topObject.ID 的包装器对象,并将事件处理程序挂钩到该对象的方法。
  • 使用闭包作用域,可以在作用域中保留对 topObject.ID 的引用(与上面的方法相同,但繁重的工作由编译器完成)

Don't change the signature of the event. While the CLR allows, technically, for any signature for an event, there are reasons why the entire Framework was designed with events having the signature (object sender, EventArgs args). In fact, there is an FxCop rule for events that violate this signature, CA1009: Declare event handlers correctly:

Event handler methods take two
parameters. The first is of type
System.Object and is named 'sender'.
This is the object that raised the
event. The second parameter is of type
System.EventArgs and is named 'e'.

There are several solutions (alternatives):

  • pass the topObject.ID as a member of your custom EventArgs.
  • create a wrapper object that encapsulates the topObject.ID and hook the event handler to a method of this object.
  • use a closure scope that can retain the reference to topObject.ID in scope (which is identical with the method above, but the heavylifting is done by the compiler)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文