通过匿名委托取消事件订阅

20

我经常使用Resharper 5.1代码分析,但经常会从resharper中得到以下提示:

"通过匿名委托取消订阅事件"

#Part of Code  

if (((bool)e.NewValue))
{
    listView.PreviewTextInput += (o,args) =>
        listView_PreviewTextInput(o,args,listView);
}
else
{
    listView.PreviewTextInput -= (o, args) => 
        listView_PreviewTextInput(o, args, listView);
}

我应该如何纠正或优化这个东西?

2个回答

34

你可以将lambda表达式提取到一个变量中:

EventHandler func = (sender, e) =>
    listView_PreviewTextInput(sender, e, listView);

if (((bool)e.NewValue))
{
    listView.PreviewTextInput += func;
}
else
{
    listView.PreviewTextInput -= func;
}

谢谢,但是EventHandler应该是具体的,对吧?因为它给我一个错误... System.EvenTArgs不能赋值给TextCompositonEventArgs - Ankesh
在这种情况下,listView.PreviewTextInput 不是一个 EventHandler,而可能是一个 EventHandler<TextCompositonEventArgs>,但由于您没有在问题中展示,所以我无法确定。 - Steven
嗯,我的错。不管怎样,谢谢... :) - Ankesh

27

警告!Steven的被接受回答错误的,它只是掩盖了Resharper正在发出警告的问题。

每次执行给定的代码时

 EventHandler func = (sender, e) =>
     listView_PreviewTextInput(sender, e, listView);

你会获得一个新的(因为你可能捕获了不同的 listView )匿名委托实例保存到 func ,这个实例还未订阅任何事件,所以最终此代码:

listView.PreviewTextInput -= func;

实际上,Jon Skeet说在某些情况下它 可能会工作:

C#规范明确表示(如果我没记错的话):如果你有两个匿名函数(匿名方法或lambda表达式),它们可以创建等效的委托.

例如,当编译器不会每次生成新实例时,你将看到良好的行为。

但这是不可靠的,肯定不能解决题目中所描述的带有捕获变量listView的情况。

所以我的建议是:

仅在您永远不需要取消订阅时将匿名函数用作事件处理程序。


那么正确的做法是什么? - Daanvl
2
@Daanvl 将匿名委托转换为“普通”方法或本地函数。 - Shorstok
1
注意:在原始情况下无法“将其转换为普通方法”,因为有额外的参数listView。可能是本地函数,但我不确定本地函数是否比lambda更可靠,因为在本地函数中有这样的声明:“请注意,当一个本地函数捕获了封闭作用域中的变量时,该本地函数将被实现为委托类型。”对我来说,这可能意味着它们不是同一实例。如果附加参数不改变,则可以存储在字段中。 - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接