什么是.NET对象的生命周期?

14

.NET中对象的生命周期是什么?

据我所知,它是:

  1. 对象创建 - 调用构造函数(如果存在)
  2. 使用方法/属性/字段
  3. 对象销毁 - 调用Dispose(如果存在)
  4. 垃圾回收器在某个时刻调用析构函数

2
注意,它不叫做“析构函数”,而是“终结器”。 - 1800 INFORMATION
2
2是可选的... object通常用作同步锁,但它从不访问任何成员。 - Marc Gravell
9个回答

22

Dispose方法不会自动调用,你需要手动调用它,或者使用using块,例如:

using(Stream s = File.OpenRead(@"c:\temp\somefile.txt"))
    // Do something with s

如果存在终结器,GC才会调用它。拥有终结器会导致您的类在2个步骤中被收集;首先将对象放入终结器队列,然后调用终结器并收集对象。没有终结器的对象会直接被收集。

准则是Dispose处理托管和非托管资源,而终结器仅清理非托管资源。当Dispose方法释放了非托管资源时,可以调用GC.SuppressFinalize以避免对象长时间存活并进入终结器队列。请参见MSDN以获取正确的dispose模式示例。


17

就像一个边界情况一样...你可以在不使用构造函数的情况下创建对象:

class Foo {  
    public Foo() {
        message += "; ctor";
    }
    string message = "init";
    public string Message { get { return message; } }
}
static class Program {
    static void Main() {
        Foo foo = new Foo();
        Console.WriteLine(foo.Message); // "init; ctor"
        Foo bar = (Foo)System.Runtime.Serialization.FormatterServices
            .GetSafeUninitializedObject(typeof(Foo));
        Console.WriteLine(bar.Message); // null
    }
}

1
我看不出你何时或应该使用它,但+1教会了我一些很酷的东西。 - rein
3
这主要是在反序列化过程中由BinaryFormatter使用。 - Marc Gravell

6
以下是我了解的步骤:
  1. 加载程序集
  2. 执行静态初始化器
  3. "new"调用:
    1. 分配内存
    2. 执行非静态初始化器
    3. 执行构造函数
  4. 实例现在已准备好供使用
  5. 当对象的最后一个引用消失后: 如果该对象没有终结器,则现在可以进行回收;如果该对象有终结器,则将其放入终结器队列中。
  6. (可选)终结器队列中的对象会在一个特殊线程中调用它们的终结器;如果应用程序仍未引用该对象,则它也变得可以进行垃圾回收。
  7. 垃圾收集器释放内存
正如其他人已经指出的那样,由于运行时不会对其进行操作,因此用户必须调用Dispose()。

1
应该指出的是,如果终结器运行时间过长,则线程将被中止,而没有任何通知或抛出异常。 - Matthew Whited
静态构造函数不必按照特定顺序进行初始化。我认为唯一的保证是它们在访问该类中的静态变量之前被调用。但这可能发生在构造函数之后。 - Bert Huijben
http://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx 明确说明:"在创建第一个实例或引用任何静态成员之前自动调用"。 - David Schmitt

5
这里有一个关于该问题的详细描述。首先,运行时不会调用Dispose方法,您需要自己调用它。也不存在析构函数,但是有终结器:如果对象重写了Finalized方法,则在对象对应用程序不再可访问时将调用该方法。可能会发生在终结期间对象再次变得可访问(例如,在全局对象中存储对自身的引用),因此它返回到您模型的第2步。还有GC对象中的方法可以让用户控制对象的终结。

我认为终结器不再允许触及托管空间。至少这是我对官方IDispose模式的理解,该模式明确区分了终结器的托管和非托管资源。 - David Schmitt
1
他们可以这样做,但只是不这样做。请参见http://blogs.msdn.com/clyon/archive/2006/04/25/583698.aspx。 - Sander Rijken

1
以下是一个示例类,它使用了这里提供的所有信息。我已经花了数小时进行测试,这是对我最有效的方法。
/*********************************
 * Author:  Theofanis Pantelides *
 *   Date:  23 Jun 2009          *
 *********************************/

using System;
using System.IO;

public class MyClass : IDisposable
{
    String oFile;
    Stream oStream;

    public MyClass(String _File)
    {
        oStream = File.OpenRead(oFile = _File);
        // Initialize
    }

    ~MyClass()
    {
        this.Dispose();
        // Destruct
    }

    public void doSomething()
    {
        // do Whatever it is you are trying to do
    }

    #region IDisposable Members

    /// <summary>
    /// Dispose all resources used by instance of class
    /// and update Garbage Collector information
    /// </summary>
    public void Dispose()
    {
        if (oStream != null)
        {
            oStream.Dispose(); // Dispose using built in functions
            GC.SuppressFinalize(oStream); // No need for Garbage Collector
        }

        oStream = null;  // Nullify it.
    }

    #endregion
}

使用方法:

using(MyClass mc = new MyClass(@"c:\temp\somefile.txt"))
{
  mc.doSomething();
}

你甚至可以两次使用相同的声明,因为它在“using”之外不存在。

using(MyClass mc = new MyClass(@"c:\temp\somefile.txt"))
{
  mc.doSomething();
}

using(MyClass mc = new MyClass(@"c:\temp\somefile.txt"))
{
  mc.doSomething();
}

如果您希望一个类来解释整个生命周期,您应该添加一个静态字段和静态构造函数。 - Matthew Whited

1

对象的生命周期

创建对象:使用new关键字实例化新对象。

  1. 分配一块内存。这块内存足够大以容纳该对象。(CLR处理托管对象的内存分配)
  2. 将内存块转换为对象。初始化对象。(您可以通过实现构造函数来控制此步骤)

销毁对象:使用销毁来回收该对象使用的任何资源。

  1. 清理对象;例如,释放应用程序使用的任何非托管资源,如文件句柄和数据库连接。(您可以通过实现析构函数来控制此步骤)
  2. 回收所使用的对象内存。

CLR处理托管对象使用的内存释放;但是,如果您使用非托管对象,则可能需要手动释放这些项目使用的内存。


1
关于构造函数的一点说明:
每个类都有一个构造函数,如果您没有自己编写它,编译器将生成一个构造函数。除非另有规定,否则第一件事情是调用其父类型的构造函数。

1

0) 如果对象中存在静态构造函数,则在首次创建或引用该类型的对象时调用它。


0
在C#中,成员初始化器在构造函数之前被调用,而在VB.NET中则是在构造函数之后被调用。
运行时不能保证调用Finalize
Dispose和Finalize仅用于清理非托管资源。试图通过在终结器中清理托管资源(例如,在内部成员上调用Dispose)会导致问题,因为它们可能已经被终结了。
我喜欢保持简单,只使用终结器来检测并记录一个恶意错误消息,告诉开发人员去修复代码。尝试确定是否安全地执行Dispose的工作很容易出错,通常不值得花费时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接