C# .NET事件在被调用方超出范围后回调:安全做法?
(如果之前出现过此问题,我表示歉意,因为我已经搜索了,但没有找到我的搜索词的任何内容) 鉴于以下情况:C# .NET事件在被调用方超出范围后回调:安全做法?,c#,.net,garbage-collection,C#,.net,Garbage Collection,(如果之前出现过此问题,我表示歉意,因为我已经搜索了,但没有找到我的搜索词的任何内容) 鉴于以下情况: void Method1 { Foo _foo = new Foo(); _foo.DataReady += ProcessData(); _foo.StartProcessingData(); } void ProcessData() { //do something } StartProcessingData()是一个长时间运行的方法,它最终(异步)引发Data
void Method1 {
Foo _foo = new Foo();
_foo.DataReady += ProcessData();
_foo.StartProcessingData();
}
void ProcessData() { //do something }
StartProcessingData()
是一个长时间运行的方法,它最终(异步)引发DataReady
事件。(比如说它打了个服务电话之类的)
现在,\u foo
过去是一个类级变量,事件过去连接到构造函数中
然而,内存分析强调了这将如何将\u foo
及其所有依赖项永久保留在内存中,因此我对上述内容进行了更改
我的问题是:有没有过GC会毁掉一切的情况Method1
很快就完成了(当然是在事件触发之前),这意味着\u foo
停止了。但是,这是否意味着(因为_foo保留其事件的引用)ProcessData()
永远不会触发?或者,事件的存在是否足以在方法结束后保持\u foo的活动状态,足以确保ProcessData触发?还是没有定论
[在测试中,它工作得很好-ProcessData
总是被调用。即使使StartProcessingData
花费很长时间,并且中途强制GC收集(使用RedGate的内存分析器)也没有将其删除。但我想确定一下!]
为了澄清:StartProcessingData()
立即返回。Foo
对象类似于:
class Foo
{
SomeSerice _service;
event EventHandler<EventArgs> DataReady;
Foo()
{
_service = new SomeService();
_service.ServiceCallCompleted += _service_ServiceCallCompleted;
}
void StartProcessingData()
{
_service.ServiceCallAsync();
}
void _service_ServiceCallCompleted
{
DataReady(null,e);
}
让我们假设
StartProcessingData()
是完全同步的(即不涉及线程)。它将在事件激发后才会返回,并且将从\u foo.StartProcessingData()
中调用ProcessData()
。如果要验证这一点,请在ProcessData()
中放置一个断点,然后查看调用堆栈
因此,也就是说,\u foo
在触发事件并调用处理程序时不会超出范围,因为Method1()
尚未返回
现在,如果涉及线程,这意味着在另一个线程中执行的代码必须包含对\u foo
的引用;否则,事件将不可能被触发。因此,\u foo
仍然不是垃圾收集的候选对象。因此,无论哪种情况,您都不必担心垃圾被收集
(编辑)
挂接
\u service
的ServiceCallCompleted
事件现在意味着\u service
包含对\u foo
的引用,防止垃圾收集。对象Foo
可以从对象Bar
订阅事件,然后放弃对Bar
的所有引用,在大多数情况下不会影响Bar
触发事件的能力,因为Bar
可能会由于某个线程或外部对象而触发其事件,这只有在该线程或外部对象引用它时才会发生。有两个原因可能很重要Foo
保留对Bar
的引用;这两种情况都不可能适用于你的情况
就我个人而言,我认为让对象在放弃订阅之前取消所有订阅是个好主意。人们可能会认为,在放弃订阅服务器时,事件发布服务器将超出范围,但如果事件保持连接,则任何使发布服务器保持活动状态的东西都将无用地使被放弃的订阅服务器保持活动状态。如果这些被抛弃的订阅者中的任何一个同时也是事件发布者,那么他们被抛弃的订阅者可能会被无用地保留下来,等等。StartProcessingData是静态的,这是一种打字错误吗?或者你的意思是把_foo.StartProcessingData()放进去是的,这是个打字错误。现在修复:谢谢:)我不确定我是否完全理解你。我假设
StartProcessingData
将实际调用处理程序。如果是这种情况,Method1
将不会像现在的代码那样在调用完成之前完成。抱歉,我没有说明StartProcessingData是否会异步引发事件。我将编辑原始文件。一旦StartProcessingData()
完成,在引发事件之前,当\u foo
不存在时,在这段时间内会发生什么?我原本以为事件不会被引发,因为它已经不存在了,但事实似乎并非如此。如果StartProcessingData()
是同步的,那么您误解了执行顺序StartProcessingData()
在触发事件之前不会返回。如果涉及线程,则另一个线程持有对\u foo
的引用,并防止其被垃圾收集。将代码发布到Foo
对象可能会有所帮助。StartProcessingData将立即返回。我将为Foo对象发布一些psedoo代码。听你们两个说的:这就是我困惑的原因_福保留了推荐信
class Program
{
static void Main(string[] args)
{
Class1 _class1 = new Class1();
Console.WriteLine("Disposing of Class 1");
_class1 = null;
GC.Collect();
System.Threading.Thread.Sleep(15000);
Console.Read();
}
}
internal class Class1
{
internal Class1()
{
Foo _foo = new Foo();
_foo.DataReady += new EventHandler<EventArgs>(_foo_DataReady);
_foo.StartProcessingData();
}
void _foo_DataReady(object sender, EventArgs e)
{
Console.WriteLine("Class 1 Processing Data");
}
}
class Foo
{
internal event EventHandler<EventArgs> DataReady = delegate { };
internal void StartProcessingData()
{
System.Threading.Timer _timer = new System.Threading.Timer(OnTimer);
Console.WriteLine("Firing event in 10 secs");
_timer.Change(10000, System.Threading.Timeout.Infinite);
}
private void OnTimer(object state)
{
DataReady(this, null);
}
}
Firing event in 10 secs
Disposing of Class 1
Class 1 Processing Data