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

发布于 2024-08-03 21:12:50 字数 606 浏览 4 评论 0原文

我有以下代码:

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;
}  

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

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

我希望我的问题很清楚。

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.

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?)

I hope my question was clear.

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

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

发布评论

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

评论(4

我的影子我的梦 2024-08-10 21:12:50

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

  • 无法按编码从事件中删除匿名委托。
  • 匿名委托的生命周期将比调用它的方法的生命周期长,因为您已将其添加到 FoundStep 中,而它是 this 的成员。
  • FindStepsByType 中的每个条目都会向 FoundStep 添加另一个匿名委托。
  • 匿名委托是一个闭包,可以有效地延长 retval 的生命周期,因此即使您在代码中的其他地方停止引用 retval,它仍然由匿名委托持有。

要解决此问题,并仍然使用匿名委托,请将其分配给局部变量,然后删除 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;
  }

使用 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;
    }

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 FoundStep which is a member of this.
  • Every entry into FindStepsByType adds another anonymous delegate to FoundStep.
  • The anonymous delegate is a closure and effectively extends the lifetime of retval, so even if you stop referencing retval elsewhere in your code, it's still held by the anonymous delegate.

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

  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:

    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;
    }
你的呼吸 2024-08-10 21:12:50

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

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();

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();
冬天旳寂寞 2024-08-10 21:12:50

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

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

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. res in your example) and keep them alive (prevents garbage collector from collecting them) too.

追我者格杀勿论 2024-08-10 21:12:50

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

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

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

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

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.

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").

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文