Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在对象成为孤立对象之前,是否需要从对象中删除事件订阅?_C#_.net_Events_Garbage Collection - Fatal编程技术网

C# 在对象成为孤立对象之前,是否需要从对象中删除事件订阅?

C# 在对象成为孤立对象之前,是否需要从对象中删除事件订阅?,c#,.net,events,garbage-collection,C#,.net,Events,Garbage Collection,如果我的软件有两个对象实例,其中一个订阅了另一个的事件。我是否需要在它们成为孤儿之前取消彼此的订阅,以便垃圾收集器清理它们?或者我是否有任何其他原因需要清除事件关系?如果订阅的对象是孤立的,但订阅者不是孤立的,或者反之亦然,该怎么办?是的。事件发布者持有对这些对象的引用,这将防止它们被垃圾收集 让我们看一个例子,看看会发生什么。我们有两个班;一个公开事件,另一个使用它: class ClassA { public event EventHandler Test; ~ClassA(

如果我的软件有两个对象实例,其中一个订阅了另一个的事件。我是否需要在它们成为孤儿之前取消彼此的订阅,以便垃圾收集器清理它们?或者我是否有任何其他原因需要清除事件关系?如果订阅的对象是孤立的,但订阅者不是孤立的,或者反之亦然,该怎么办?

是的。事件发布者持有对这些对象的引用,这将防止它们被垃圾收集

让我们看一个例子,看看会发生什么。我们有两个班;一个公开事件,另一个使用它:

class ClassA
{
    public event EventHandler Test;
    ~ClassA()
    {
        Console.WriteLine("A being collected");
    }
}
class ClassB
{
    public ClassB(ClassA instance)
    {
        instance.Test += new EventHandler(instance_Test);
    }

    ~ClassB()
    {
        Console.WriteLine("B being collected");
    }

    void instance_Test(object sender, EventArgs e)
    {
        // this space is intentionally left blank
    }
}
注意ClassB如何不存储对ClassA实例的引用;它只是挂接一个事件处理程序

现在,让我们看看如何收集对象。情景1:

ClassB temp = new ClassB(new ClassA());
Console.WriteLine("Collect 1");
GC.Collect();
Console.ReadKey();
temp = null;
Console.WriteLine("Collect 2");
GC.Collect();
Console.ReadKey();
我们创建一个ClassB实例,并通过temp变量保存对它的引用。它被传递给一个新的ClassA实例,在这里我们不在任何地方存储对它的引用,因此在ClassB构造函数完成后,它立即超出范围。我们让垃圾收集器在ClassA超出范围时运行一次,在ClassB超出范围时运行一次。输出:

Collect 1
A being collected
Collect 2
B being collected
Collect 1
Collect 2
B being collected
A being collected
情景2:

ClassA temp = new ClassA();
ClassB temp2 = new ClassB(temp);
temp2 = null;
Console.WriteLine("Collect 1");
GC.Collect();
Console.ReadKey();
temp = null;
Console.WriteLine("Collect 2");
GC.Collect();
Console.ReadKey();
创建ClassA的一个新实例,并在temp变量中存储对它的引用。然后创建一个新的ClassB实例,将temp中的ClassA实例传递给它,我们将对它的引用存储在temp2中。然后我们将temp2设置为null,使ClassB实例超出范围。与前面一样,我们在每个实例超出范围后运行垃圾收集器。输出:

Collect 1
A being collected
Collect 2
B being collected
Collect 1
Collect 2
B being collected
A being collected

因此,总结一下;如果公开事件的实例超出范围,则无论是否连接了事件处理程序,它都可用于垃圾收集。如果一个实例的事件处理程序连接到另一个实例中的事件,则在分离该事件处理程序之前,该实例将不可用于垃圾收集,或者事件处理程序所连接的实例可用于垃圾收集。

如果公开事件的对象是长期存在的,则只需取消钩住事件,否则钩住事件的对象将是短期存在的(并且可以相当快地收集垃圾)

在这种情况下,未能解除挂钩将导致相当于内存泄漏的情况,因为您的短期对象将无法进行GCD——因为长期对象中的事件保留在委托上,委托保存对短期对象的引用。由于该委托仍引用该短期对象,因此无法对其进行垃圾收集

根据定义,静态事件是长期存在的——它们一直存在到程序退出。如果你钩住了一个静态事件,你肯定应该在完成后把它解开


如果两个对象都将成为孤立对象,则无需取消挂钩。

订阅事件会导致强烈引用订阅方。这是因为,在封面下,事件是委托,而实例方法的委托是对象引用和实际方法的组合。如果您不取消订阅,发布者将继续维护引用,并且只要发布者还活着,订阅对象就不会真正孤立(和GC'ed)

反之亦然,即订阅的对象没有对发布者的任何引用