ReSharper / C#中的“代理减法具有不可预测的结果”?


124

使用myDelegate -= eventHandlerReSharper(版本6)问题时:

委托减法具有不可预测的结果

JetBrains在解释了背后的原因。该解释是合理的,并且在阅读之后,我怀疑我对-on委托的所有使用。

那么如何

  • 我可以编写一个非自动事件而不会使ReSharper脾气暴躁吗?
  • 或者,是否有更好和/或“正确”的方法来实现这一目标?
  • 或者,我可以忽略ReSharper吗?

这是简化的代码:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

单声道发出相同的警告。这是R#对问题confluence.jetbrains.com/display/ReSharper/…的描述(仅适用于代表列表)
thoredge,2015年

Answers:


134

不要害怕!ReSharper警告的第一部分仅适用于删除代表列表。在您的代码中,您总是要删除一个委托。第二部分讨论删除重复的委托后的委托顺序。事件并不能保证其订阅者的执行顺序,因此也不会真正影响您。

由于上述机制可能导致不可预测的结果,因此ReSharper每当遇到委托减法运算符时都会发出警告。

ReSharper发出此警告是因为多播委托减法可能会引起麻烦,但这并不是完全谴责该语言功能。幸运的是,这些陷阱处于边缘情况,如果您只是在检测简单事件,就不太可能遇到它们。没有更好的方法来实现自己的add/ remove处理程序,您只需要注意即可。

我建议将ReSharper对该消息的警告级别降级为“提示”,以免使您对它们的警告不敏感,这通常是有用的。


66
我认为R#将结果称为“不可预测的”是不好的。明确指定了它们。“不是用户可能预测的”与“不可预测的”完全不同。(这也是不准确的说,.NET Framework定义重载-这是烤成C#编译器Delegate没有超载+-。)
乔恩斯基特

6
@乔恩:我同意。我想每个人都已经习惯了微软为其设定的高标准。波兰语水平很高,在.NET世界中,有很多事情使您“跌入成功之坑”,遇到的语言功能仅是在有坑的坑旁轻快地走可能您错过的机会被某些人认为是令人讨厌的,并保证标语上的意思PIT OF SUCCESS IS THAT WAY --->
Allon Guralnek 2012年

5
@AllonGuralnek:另一方面,您是什么时候最后一次听说有人因此而出问题?
乔恩·斯基特

5
@Jon:听说有问题吗?在发布此问题之前,我什至不知道这种行为。
Allon Guralnek 2012年

2
奇怪的是,R#会警告委托减法,而不警告事件的完全实现,而事件的完全相同。核心问题是.net使用了一个单一的Delegate.Combine“扁平化”多播委托,因此,如果给它分配了委托[X,Y]和Z,则无法确定结果是[X,Y,Z]还是[[ X,Y],Z](后一个委托将[X,Y]委托作为其Target,并将该委托的Invoke方法作为其Method)。
2013年

28

您不应该直接使用委托求和或减去。而是你的领域

MyHandler _myEvent;

相反,也应该将其声明为事件。这样可以解决问题而不会冒解决方案的风险,并且仍然可以使用事件。

event MyHandler _myEvent;

使用委托总和或减法很危险,因为在简单分配委托时您可能会丢失事件(按照声明,开发人员不会像声明为事件那样直接推断这是一个多播委托)。只是为了举例说明,如果此问题中提到的属性未标记为事件,则下面的代码会将第一个前两个分配设为LOST,因为有人只是分配给了委托(这也是有效的!)。

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

分配Method3时,我完全失去了两个初始订阅。事件用法将避免此问题,并同时删除ReSharper警告。


我从没想过要这样做,但是只要您保留该底层事件的私有性,就可以保护事件委托的使用。但是,当在添加/删除处理程序中必须进行更特定的线程同步(例如,多个事件订阅或子订阅也需要跟踪)时,此方法将无法正常工作。但是,无论如何它都会删除警告。
杰里米

-17

将其设置为= null而不是使用-=


5
remove事件的方法不应删除所有处理程序,而应删除请求删除的处理程序。
Servy

如果他只添加了一个,那么如果他实际上只删除了一个,则将它们全部删除。我并不是说在所有情况下都仅将其用于此特定的转发器消息。
乔纳森·贝雷斯福德

6
但是您知道委托总是删除调用列表中的唯一项。通过将正确的工作代码转换为不正确的损坏代码(在某些情况下会同时起作用),这会使重新发布消息消失,但是在许多情况下,这将以异常且难以诊断的方式中断。
Servy

我不得不对此表示反对,因为-17对于花一些时间写“潜在”答案的人来说太严厉了。+1表示您不被动,即使您输入的内容不正确。
John C
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.