C# 使用对象初始值设定项时的复活差异

C# 使用对象初始值设定项时的复活差异,c#,garbage-collection,finalizer,C#,Garbage Collection,Finalizer,我有以下代码: 本质上,我试图演示c#finalizer的使用,并制作一个不会消亡的对象,我称之为僵尸。现在,通常这个演示效果很好,但今天我尝试使用相同的代码和对象初始值设定项,而不是只分配给属性(在本例中是名称)。我注意到有一点不同。也就是说,终结器永远不会被调用,即使在我尽力让垃圾收集器完成它的工作时也不会 有人能解释一下区别吗?或者我在C#编译器中发现了一个bug吗 (我在Win7x64上的VS2010 SP1中使用C#4) 谢谢 using System; using System.Co

我有以下代码:

本质上,我试图演示c#finalizer的使用,并制作一个不会消亡的对象,我称之为僵尸。现在,通常这个演示效果很好,但今天我尝试使用相同的代码和对象初始值设定项,而不是只分配给属性(在本例中是名称)。我注意到有一点不同。也就是说,终结器永远不会被调用,即使在我尽力让垃圾收集器完成它的工作时也不会

有人能解释一下区别吗?或者我在C#编译器中发现了一个bug吗

(我在Win7x64上的VS2010 SP1中使用C#4)

谢谢

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Zombie
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Main thread: " + Thread.CurrentThread.ManagedThreadId);

              // case 1: this is where the problem is located.
      Zombie z = new Zombie { Name = "Guy" }; // object initializer syntax makes that the finalizer is not called.

              // case 2: this is not causing a problem. The finalizer gets called.
      //Zombie z = new Zombie();
      //z.Name = "Guy";

      WeakReference weakZombieGuyRef = new WeakReference(z, true);

      z = null;

      GC.GetTotalMemory(forceFullCollection: true);

      GC.Collect();

      while (true)
      {

        Console.ReadKey();
        if (weakZombieGuyRef.IsAlive)
        {
          Console.WriteLine("zombie guy still alive");
        }
        else
        {
          Console.WriteLine("Zombie guy died.. silver bullet anyone?");
        }

        Zombie.Instance = null;

        GC.AddMemoryPressure(12400000);
        GC.GetTotalMemory(forceFullCollection: true);

        GC.Collect();
      }


    }
  }

  public class Zombie
  {
    public string Name { get; set; }
    public  static Zombie Instance = null;

    ~Zombie()
    {
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
      Console.WriteLine("Finalizer called on zombie" + this.Name);
      lock (typeof(Zombie))
      {
        Instance = this;

        GC.ReRegisterForFinalize(this);
      }
    }
  }
}

如果您希望对象不会消亡,那么您真的不需要使用终结器。只需拥有所有实例的私有静态列表,并在创建对象时将其添加到该列表中:

class Immortal
{
    static List<Immortal> _immortals = new List<Immortal>();

    public Immortal()
    {
       _immortals.Add(this);
    }
}
类不朽
{
静态列表_=新列表();
公共图书馆()
{
_加上(这个);
}
}

编辑:虽然下面的原始答案仍然准确,但似乎是调试信息和优化的混合在一起造成了不同

根据我的实验:

Compiler flags                        Result
/o+ /debug-                           Finalizer runs
/o+ /debug+                           Finalizer runs
/o- /debug-                           Finalizer runs
/o- /debug+                           Finalizer does *not* run

当使用
/o+
在命令行上编译时,终结器仍在我的框中调用。我的猜测是,您正在一个调试器中运行-它改变了GC行为。如果没有调试器,GC将收集它可以证明永远不会被读取的任何内容。使用调试器,我相信GC不会收集堆栈上仍然有引用的任何对象,即使没有代码来读取有问题的变量

现在使用对象初始值设定项,编译器代码在堆栈上包含一个额外的引用。这一行:

Zombie z = new Zombie { Name = "Guy" };
实际上:

Zombie tmp = new Zombe();
tmp.Name = "Guy";
Zombie z = tmp;
z
的赋值仅在设置了所有属性后执行


我猜这里的
tmp
变量使对象保持活动状态。

感谢您的快速回答。但是,当我使用或不使用调试器(F5或Ctrl-F5)运行时,没有区别。堆栈上的tmp变量确实可以解释它。@jeroentrappers:您是在调试配置中构建还是在发布配置中构建?(我看到的是“终结器调用僵尸家伙”,如果这是您期望的,但没有看到…)我确实是在调试中构建的。在版本中构建时,问题会自动消失。谢谢。我意识到,不过演示的目的是演示终结器。当我用一个额外的方法创建僵尸z时,问题确实消失了。public static Zombie CreateZombie(){return new Zombie{Name=“Guy”};}在VS2012和.NET 4.5中尝试了这一点。我找不到任何在对象初始值设定项和“旧式”先创建然后设置属性代码之间存在差异的情况。如果在调试模式下执行此操作而不进行优化,则在方法结束之前,
GC
将不会收集
z
(即最初引用的对象
z
)。这便于调试;直到局部变量以一种正式的方式超出范围(
z
仍在方法末尾的范围内),它才会收集。如果将
Main
(带
z
)的第一部分提取到一个单独的方法中,它也将在调试构建下工作。