1

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

我有一个事件处理程序,它附加到一个对象,该对象是另一个类的属性。在我的事件处理程序中,我需要有关导致事件的对象的其他元数据(即包含它的对象的 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;

我知道该操作无法强制转换为事件处理程序。是否有一些简单的方法可以进行这种转换,仍然可以确保我可以分离事件处理程序?我只是在想这个/有没有我现在看不到的方法来做到这一点?

4

4 回答 4

1

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

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

于 2010-12-08T23:26:17.237 回答
0

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

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 类的工作方式。这种方法有什么问题吗?

于 2010-12-08T23:34:59.040 回答
0

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

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

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

     // do something with id...
}

我会在发送者和参数的约定中保留事件签名。

于 2010-12-08T23:37:29.450 回答
0

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

事件处理程序方法采用两个参数。第一个是 System.Object 类型,名为“发送者”。这是引发事件的对象。第二个参数的类型为 System.EventArgs,名为“e”。

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

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