C#-匿名函数和事件处理程序
我有以下代码:C#-匿名函数和事件处理程序,c#,scope,anonymous-methods,C#,Scope,Anonymous Methods,我有以下代码: public List<IWFResourceInstance> FindStepsByType(IWFResource res) { List<IWFResourceInstance> retval = new List<IWFResourceInstance>(); this.FoundStep += delegate(object sender, WalkerStepEventArgs e)
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;
}
public List FindStepsByType(IWFResource res)
{
List retval=新列表();
this.FoundStep+=委托(对象发送方,WalkerStepEventArgs e)
{
如果(e.Step.ResourceType==res)retval.Add(e.Step);
};
这个。Start();
返回返回;
}
注意我是如何将我的事件成员(FoundStep)注册到本地就地匿名函数的
我的问题是:当函数“FindStepByType”将结束时,匿名函数将自动从事件的委托列表中删除,还是我必须在退出函数之前手动删除它?(我该怎么做?)
我希望我的问题是清楚的 否,它不会自动删除。从这个意义上讲,匿名方法和“正常”方法之间没有区别。如果需要,您应该手动取消订阅活动
实际上,它还将捕获其他变量(例如,在您的示例中,
res
),并使它们保持活动状态(防止垃圾收集器收集它们) 使用匿名委托(或lambda表达式)订阅事件时,不允许您稍后轻松取消订阅该事件。事件处理程序永远不会自动取消订阅
如果查看代码,即使您在函数中声明并订阅了事件,您订阅的事件也在类中,因此一旦订阅,即使在函数退出后,它也将始终被订阅。要实现的另一件重要事情是,每次调用此函数时,它都会再次订阅事件。这是完全合法的,因为事件本质上是多播委托,允许多个订户。(这可能不是你想要的。)
为了在退出函数之前取消订阅委托,需要将匿名委托存储在委托变量中,并将委托添加到事件中。然后,您应该能够在函数退出之前从事件中删除委托
出于这些原因,如果您必须在以后某个时间取消订阅活动,则不建议使用匿名代理。请参阅(特别是标题为“使用匿名方法订阅事件”的部分)。您的代码存在一些问题(您和其他人已经发现了一些问题):
- 无法按编码从事件中删除匿名委托
- 匿名委托的寿命将比调用它的方法的寿命长,因为您已将其添加到FoundStep,它是this的成员
- FindStepByType中的每个条目都会向FoundStep添加另一个匿名委托
- 匿名委托是一个闭包,它有效地延长了retval的生存期,因此即使您停止在代码中的其他地方引用retval,它仍然由匿名委托持有
public List FindStepsByType(IWFResource res)
{
List retval=新列表();
EventHandler处理程序=(发送方,e)=>
{
如果(e.Step.ResourceType==res)retval.Add(e.Step);
};
this.FoundStep+=处理程序;
尝试
{
这个。Start();
}
最后
{
this.FoundStep-=处理程序;
}
返回返回;
}
使用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;
}
public List FindStepsByType(IWFResource res)
{
var retval=新列表();
无效处理程序(对象发送方,WalkerStepEventArgs e)
{
如果(e.Step.ResourceType==res)retval.Add(e.Step);
}
FoundStep+=处理程序;
尝试
{
这个。Start();
}
最后
{
FoundStep-=处理程序;
}
返回返回;
}
以下是如何以匿名方式取消订阅事件的方法:
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();
这和使用谓词不一样吗?当我使用谓词时,我不会释放谓词委托。谓词不会保存在任何地方,但在这里,您订阅了一个事件。只要包含事件的对象处于活动状态,它就会保存对委托的引用并间接引用其变量。当您将say,
.Where(x=>x.Hidden)
传递给某个方法时,该方法将使用它完成工作并将其丢弃(就方法而言,它只是一个局部变量。你的情况并非如此。此外,如果方法将它存储在某个地方,你也应该担心这一点。局部函数,实现同样的效果?你的意思是同样的问题吗?
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();