C# - 匿名函数和事件处理程序(C# - anonymous functions and even

2019-08-04 04:12发布

我有以下代码:

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”将结束 - 将匿名函数从该事件的委托列表中自动删除或我有steping出功能之前,需要手动删除它? (怎么做呢?)

我希望我的问题是清楚的。

Answer 1:

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

  • 匿名委托可以作为编码无法从活动中移除。
  • 匿名委托将活得比方法调用它,因为你已经把它添加到FoundStep就是一个部件的寿命。
  • 每一个进入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;
    }


Answer 2:

下面是关于匿名方法如何退订事件处理方法:

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


Answer 3:

不,它不会被自动删除。 在这个意义上,有没有匿名方法和“正常”的方法之间的差异。 如果你想,你应该手动从事件退订。

实际上,它会捕获其他变量(如res在你的例子),并让他们活着(防止垃圾收集器收集来自他们)了。



Answer 4:

当使用匿名委托(或lambda表达式)订阅事件不会让您轻松地从该事件后来取消。 事件处理程序是永远不会自动取消。

如果你看一下你的代码,即使你声明和订阅事件的功能,你订阅的事件是在类,所以一旦订阅,它会永远甚至在函数退出后认购。 实现其他重要的是,每一个该函数被调用时,它会再次订阅事件。 这是完全合法的,因为事件本质上是多路广播委托,并允许多个用户。 (这可能是也可能不是你想要什么。)

为了从委托退订退出功能之前,你需要匿名委托存储在一个委托变量和委托添加到该事件。 然后,您应该能够在函数退出之前删除从该事件的委托。

由于这些原因,如果你将不得不在某个点以后从事件退订,不推荐使用匿名委托。 请参阅如何:订阅和退订事件(C#编程指南) (特别是标题为“要通过使用匿名方法订阅事件”)。



文章来源: C# - anonymous functions and event handlers