C# - 匿名函数和事件处理程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1389543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 15:57:32  来源:igfitidea点击:

C# - anonymous functions and event handlers

c#scopeanonymous-methods

提问by Adi Barda

I have the following code:

我有以下代码:

public List<IWFResourceInstance> FindStepsByType(IWFResource res)  
{  
    List<IWFResourceInstance> retval = new List<IWFResourceInstance>();  
    this.FoundStep += delegate(object sender, WalkerStepEventArgs e)   
                      {   
                        if (e.Step.ResourceType == res) retval.Add(e.Step);   
                      };  
    this.Start();  
    return retval;
}  

Notice how I register my event member (FoundStep) to local in-place anonymous function.

请注意我如何将我的事件成员 (FoundStep) 注册到本地就地匿名函数。

My question is: when the function 'FindStepByType' will end - will the anonymous function be removed automatically from the delegate list of the event or I have to manually remove it before steping out the function? (and how do I do that?)

我的问题是:函数“FindStepByType”何时结束 - 匿名函数是否会自动从事件的委托列表中删除,或者我必须在退出函数之前手动删除它?(我该怎么做?)

I hope my question was clear.

我希望我的问题很清楚。

采纳答案by Kit

Your code has a few problems (some you and others have identified):

您的代码有一些问题(您和其他人已经确定了一些问题):

  • The anonymous delegate cannot be removed from the event as coded.
  • The anonymous delegate will live longer than the life of the method calling it because you've added it to FoundStepwhich is a member of this.
  • Every entry into FindStepsByTypeadds another anonymous delegate to FoundStep.
  • The anonymous delegate is a closure and effectively extends the lifetime of retval, so even if you stop referencing retvalelsewhere in your code, it's still held by the anonymous delegate.
  • 匿名委托不能按照编码从事件中删除。
  • 匿名委托将比调用它的方法的生命周期更长,因为您已将它添加到FoundStep,它是this的成员。
  • 每一个进入FindStepsByType增加了一个匿名委托FoundStep
  • 匿名委托是一个闭包,有效地延长了retval的生命周期,因此即使您停止在代码中的其他地方引用retval,它仍然由匿名委托持有。

To fix this, and still use an anonymous delegate, assign it to a local variable, and then remove the handler inside a finallyblock (necessary in case the handler throws an exception):

为了解决这个问题,并仍然使用匿名委托,将它分配给一个局部变量,然后在finally块中删除处理程序(在处理程序抛出异常的情况下是必要的):

  public List<IWFResourceInstance> FindStepsByType(IWFResource res)
  {
     List<IWFResourceInstance> retval = new List<IWFResourceInstance>();
     EventHandler<WalkerStepEventArgs> handler = (sender, e) =>
     {
        if (e.Step.ResourceType == res) retval.Add(e.Step);
     };

     this.FoundStep += handler;

     try
     {
        this.Start();
     }
     finally
     {
        this.FoundStep -= handler;
     }

     return retval;
  }

With C# 7.0+ you can replace the anonymous delegate with a local function, achieving the same effect:

在 C# 7.0+ 中,您可以用本地函数替换匿名委托,实现相同的效果:

    public List<IWFResourceInstance> FindStepsByType(IWFResource res)
    {
        var retval = new List<IWFResourceInstance>();

        void Handler(object sender, WalkerStepEventArgs e)
        {
            if (e.Step.ResourceType == res) retval.Add(e.Step);
        }

        FoundStep += Handler;

        try
        {
            this.Start();
        }
        finally
        {
            FoundStep -= Handler;
        }

        return retval;
    }

回答by Mehrdad Afshari

No, it will not be removed automatically. In this sense, there's not a difference between an anonymous method and a "normal" method. If you want, you should manually unsubscribe from the event.

不,它不会被自动删除。从这个意义上说,匿名方法和“普通”方法之间没有区别。如果需要,您应该手动取消订阅该事件。

Actually, it'll capture other variables (e.g. resin your example) and keep them alive (prevents garbage collector from collecting them) too.

实际上,它也会捕获其他变量(例如res在您的示例中)并使它们保持活动状态(防止垃圾收集器收集它们)。

回答by Scott Dorman

When using an anonymous delegate (or a lambda expression) to subscribe to an event does not allow you to easily unsubscribe from that event later. An event handler is never automatically unsubscribed.

使用匿名委托(或 lambda 表达式)订阅事件时,您以后无法轻松取消订阅该事件。事件处理程序永远不会自动取消订阅。

If you look at your code, even though you declare and subscribe to the event in a function, the event you are subscribing to is on the class, so once subscribed it will always be subscribed even after the function exits. The other important thing to realize is that each time this function is called, it will subscribe to the event again. This is perfectly legal since events are essentially multicast delegates and allow multiple subscribers. (This may or may not be what you intend.)

如果您查看您的代码,即使您在函数中声明并订阅了事件,您订阅的事件也在类中,因此一旦订阅,即使在函数退出后,它也将始终被订阅。另一个需要注意的重要事情是,每次调用此函数时,它都会再次订阅该事件。这是完全合法的,因为事件本质上是多播委托并允许多个订阅者。(这可能是也可能不是您想要的。)

In order to unsubscribe from the delegate before you exit the function, you would need to store the anonymous delegate in a delegate variable and add the delegate to the event. You should then be able to remove the delegate from the event before the function exits.

为了在退出函数之前取消订阅委托,您需要将匿名委托存储在委托变量中并将委托添加到事件中。然后,您应该能够在函数退出之前从事件中删除委托。

For these reasons, if you will have to unsubscribe from the event at some later point it is not recommended to use anonymous delegates. See How to: Subscribe to and Unsubscribe from Events (C# Programming Guide)(specifically the section titled "To subscribe to events by using an anonymous method").

由于这些原因,如果您以后必须取消订阅该事件,则不建议使用匿名委托。请参见如何:订阅和取消订阅事件(C# 编程指南)(特别是标题为“使用匿名方法订阅事件”的部分)。

回答by Amit

Below is approach about how unsubscribe event in anonymous method:

下面是关于如何在匿名方法中取消订阅事件的方法:

DispatcherTimer _timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(1000);
EventHandler handler = null;

int i = 0;

_timer.Tick += handler = new EventHandler(delegate(object s, EventArgs ev)
{
    i++;
    if(i==10)
        _timer.Tick -= handler;
});

_timer.Start();