为什么我的析构函数会被调用?

时间:2009-11-30 03:11:46

标签: c# garbage-collection destructor

我有一些类通过IDictionary实例成员保存对其他类的引用。

像这样:

class A
{
  private readonly Dictionary<int, B> _particles = new Dictionary<int, B>();
  public void CreateNewB(int someInt)
   {
     var b = new B();
     if (!_particles.ContainsKey(someInt)
         _particles.Add(someInt, b);
   }
}

所以这是设置,我永远不会从这个字典中删除它们,但由于某种原因,B类的析构函数会不时地在GC上运行,我不明白为什么。

这可能与Dictionary类如何添加新引用有关吗?

固定

好的,谢谢大家的回答,我现在已经对GC和解构器有了很大的了解。

但是这个问题是我自己的,我只是添加了 someInt ,如果它已经不存在并且通过有缺陷的业务逻辑, someInt 总是1,所以第一次通过它工作,解构主义者没有得到调用。但第二次,“b”实例根本没有添加到列表中并在GC运行中被清理。

再次感谢所有帮助过的人!

6 个答案:

答案 0 :(得分:3)

如果对A类的引用已经死亡,那么你的B级将是GC

答案 1 :(得分:1)

  

我永远不会从中删除它们   字典,但由于某种原因,   B类的析构函数被称为

.NET中的析构函数与C ++中的析构函数(非托管函数)不同 析构函数自动调用Finalize方法。

以下是destructor的一些特征:

  • 无法在结构中定义析构函数。它们仅用于课程。
  • 一个类只能有一个析构函数。
  • 无法继承或重载析构函数。
  • 无法调用析构函数 。它们自动调用
  • 析构函数不接受修饰符或具有参数。

所以你的情况是这样的(用2个字):

  1. A类是垃圾收集。
  2. 由于1:_particles字段获得GC-d。
  3. 由于2:词典中的条目变为非根(可用于垃圾收集)。
  4. 作为3的结果:词典中的条目(B类实例)是GC-d。
  5.   

    这可能与如何做有关   Dictionary类添加了新的   引用?

    没有

答案 2 :(得分:1)

听起来你做的事情非常错误。在托管环境中,保持像这样基本上永久存在的引用相当于内存泄漏,除非你真的想要保留这些对象。

这里要记住的另一件事是C#中没有析构函数这样的东西。你所拥有的是终结者,它们是不同的。托管代码很少见,甚至根本不需要编写终结器。这样做的唯一理由是,当您为类型实现IDisposable以包装未被终结器覆盖的非托管资源时。

例如,很多人创建了一个实现IDisposable的类型,并将SqlConnection包装为其数据访问层的一部分。这样,他们可以使用块包装该类型的实例,并确保他们创建的任何SqlConnections都正确处理。但是这种类型需要终结器,因为底层数据库连接已经被SqlConnection类本身的终结器所覆盖。没有 new 非托管资源类型需要担心,只有SqlConnection类型。但是,如果您正在构建一个全新的数据库引擎并为其实现新的.Net数据提供程序,那么您可能希望为您的连接实现终结器。

答案 3 :(得分:0)

所以我有以下类型:

type k {
public k() { Console.WriteLine("Hi, I'm a new K!"); }
public ~k() { Console.WriteLine("I'm a dying K!"); }
}

还有一小段代码:

Dictionary<int, k> ks = new Dictionary<int, k);
for(int i=0;i<10;i++) { ks.add(i, new k()); }

你看到~k()被一次调用了?那是什么意思?

答案 4 :(得分:0)

好的,谢谢大家的回答,我现在已经对GC和解构器有了很大的了解。

但是这个问题是我自己的,我只是在它已经存在并且通过有缺陷的业务逻辑时添加someInt,someInt总是1,所以第一次通过它工作并且解构器没有被调用。但第二次,“b”实例根本没有添加到列表中并在GC运行中被清理。

我正在回答此事只是为了关闭它:)

答案 5 :(得分:-2)

我敢打赌我在其他地方创建的课程B。如果它是一个控制台应用程序,则在B构造函数和终结器中输出一些内容。确保实例化的数量符合您的预期。