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

Adi*_*rda 22 c# scope anonymous-methods

我有以下代码:

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;
}  
Run Code Online (Sandbox Code Playgroud)

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

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

我希望我的问题很明确.

Kit*_*Kit 40

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

  • 匿名代表不能以编码方式从事件中删除.
  • 匿名委托将活得比方法调用它,因为你已经把它添加到的生活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;
  }
Run Code Online (Sandbox Code Playgroud)

使用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;
    }
Run Code Online (Sandbox Code Playgroud)


Ami*_*mit 6

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

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();
Run Code Online (Sandbox Code Playgroud)


Meh*_*ari 5

不,它不会被自动删除.从这个意义上讲,匿名方法和"普通"方法之间没有区别.如果需要,您应手动取消订阅活动.

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

  • 谓词不会保存在任何地方,但在这里,您订阅了一个事件.只要包含事件的对象处于活动状态,它就会保存对您的委托的引用,并间接保存对其变量的引用.当你将`.Where(x => x.Hidden)`传递给某个方法时,该方法将使用它并将其丢弃(就`where`方法而言,它只是一个局部变量.这不适用于你的情况.另外,如果`Where`确实将它存储在某个地方,你也应该对此感到担心. (2认同)