我已经在下面编写了非常基本的程序,我是C#的新手。不会调用析构函数〜Program(),因此在输出中看不到“被调用的析构函数”字符串。我检查了其他类似的问题,但找不到我的答案。谢谢。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static System.Console;
namespace LyfeCicleObject
{
class Program
{
public Program()
{
WriteLine("Cons called");
}
~Program()
{
WriteLine("Destructor called");
}
static void Main(string[] args)
{
WriteLine("Main started");
Program p1 = new Program();
{
WriteLine("Block started");
Program p2 = new Program();
WriteLine("Block ended");
}
WriteLine("Main ended");
}
}
}
答案 0 :(得分:0)
如果要查看终结器的输出:
如果您想了解当前行为的原因:p1
变量存在于Main
方法的范围内,并且当范围变得不可及时,GC会收集该变量,我想它会在以下情况发生:程序已经停止,这意味着没有GC(将以其他方式释放内存)。
答案 1 :(得分:0)
简短的答案-您未看到“析构函数调用”输出的原因-埋在了注释的某个位置:
.NET Core在程序结束时未运行终结器
(请参阅:Finalizers (C# Programming Guide))。
.NET Framework将尝试 来执行此操作,但是.NET Core却不会。
免责声明 :我们无法知道这些声明是否将继续适用;到目前为止,这就是它们的实现方式和记录方式。
但是,根据Raymond Chen的说法,在他的帖子Everybody thinks about garbage collection the wrong way中,如果.NET Framework也没有在程序末尾运行终结器,那也不是无效的。从不同的角度说的相关报价是这样的:
编写正确的程序不能假定终结器会运行。
只要您不认为终结器会运行,那么它们的实现方式或实现方式的更改都无关紧要。
在继续使用C#之前,您必须放弃.NET中的析构函数的想法,因为它们根本不存在。 C#将C ++的析构函数 syntax 用于终结器,但相似之处就止于此。
好消息是,有一种 方法可以完成您尝试做的事情,但是需要进行范式转换,这是您对资源获取和释放方式的看法的重大变化。您是否真的需要这样做是完全不同的问题。
终结器不是释放需要及时释放的资源的唯一方法,甚至不是最好的方法。我们有一次性模式可以帮助解决这个问题。
一次性模式允许类实现者选择使用一种确定性释放资源(不包括托管堆上的内存)的通用机制。它包括终结器,但只有在对象处置不当(尤其是过程没有终止)时,才有最后清除机会。
我想说的是,与C ++析构函数相比,您将看到的主要区别是:
using
语句选择加入。您不会看到的是不必立即回收内存。
如果您想进一步了解操作方法,请继续阅读...
在进入任何代码之前,值得一提的注意事项:
Dispose(bool)
,而不是创建需要被抛弃的类层次结构的基础。例如,如果不是.Designer.cs
,则Windows窗体应用程序中的Dispose(bool)
文件会覆盖components
,以便处理null
字段。好的,代码...
以下是实现一次性模式的简单类的示例。它不提供对子类的支持,因此标记为sealed
,而Dispose(bool)
被标记为private
。
public sealed class SimpleDisposable : IDisposable
{
public SimpleDisposable()
{
// acquire resources
}
~SimpleDisposable()
{
Dispose(false);
}
public void Dispose()
{
// Suppress calling the finalizer because resources will have been released by then.
GC.SuppressFinalize(this);
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
// release managed resources
// (you don't want to do this when calling from the finalizer because the GC may have already finalized and collected them)
}
// release unmanaged resources
}
}
实际清理是通过Dispose(bool)
方法进行的。如果参数为true
,则表示正在通过IDisposable
接口(通常是using
语句,但不一定是)进行处理,并且可以清理托管资源。如果为false
,则意味着处置是作为GC扫描的一部分而进行的,因此您不能触摸托管资源,因为它们可能已经被收集。
如果您正在编写需要支持一次性模式的基类,则情况会稍有变化:Dispose(bool)
变为protected
和virtual
,因此可以被子类覆盖,但是消费者仍然无法访问。
以下是支持子类的一次性模式的基类示例。
public abstract class BaseDisposable : IDisposable
{
protected BaseDisposable()
{
// acquire resources
}
~BaseDisposable()
{
Dispose(false);
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// release managed resources
}
// release unmanaged resoures
}
}
然后是使用该支持的子类。子类也不需要实现终结器或IDisposable.Dispose
。他们要做的就是重写Dispose(bool)
,处置自己的资源,然后调用基本实现。
public class DerivedDisposable : BaseDisposable
{
public DerivedDisposable()
{
// acquire resources for DerivedDisposable
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// release DerivedDisposable's managed resources
}
// release DerivedDisposable's unmanaged resources
// Let the base class do its thing
base.Dispose(disposing);
}
}
那么处置托管和非托管资源意味着什么?
受管资源是类似于其他一次性对象甚至是非一次性对象(例如字符串)之类的东西。 BCL中的某些一次性类型会将此类字段设置为null
,以确保GC找不到对它们的有效引用。
当处置您的课程时,使用者决定不再需要它及其资源。如果您的对象包含其他一次性用品,则可以将这些对象进行处置,依此类推,因为在垃圾收集过程中不会发生这种情况。
非托管资源是诸如文件句柄,全局内存,内核对象……几乎所有通过调用操作系统分配的内容。这些不受垃圾收集器的影响,无论如何都需要释放,因此它们不受disposing
测试的约束。
如果您的一次性对象使用了另一个具有不受管理资源的一次性对象,则该对象有责任实施Disposable模式以释放其资源,并由您负责使用。
并非所有实现IDisposable
的对象实际上都具有非托管资源。通常,基类仅会支持一次性模式,因为它的作者知道至少一个派生自该类的类可能需要使用非托管资源。但是,如果某个类未实现可抛弃模式,则其子类之一可以在需要时引入该支持。
让我们稍微改变一下程序,使其达到您的期望,但是现在使用一次性模式。
注意:据我所知,让Main
创建包含Program
类的实例并不是很常见。我在这里这样做是为了使内容尽可能接近原始内容。
using System;
internal sealed class Program : IDisposable
{
private readonly string _instanceName;
public Program(string instanceName)
{
_instanceName = instanceName;
Console.WriteLine($"Initializing the '{_instanceName}' instance");
}
~Program()
{
Dispose(false);
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
Console.WriteLine($"Releasing the '{_instanceName}' instance's managed resources");
}
Console.WriteLine($"Releasing the '{_instanceName}' instance's unmanaged resources");
}
private static void Main(string[] args)
{
Console.WriteLine("Main started");
Program p0 = new Program(nameof(p0));
using (Program p1 = new Program(nameof(p1)))
{
Console.WriteLine("Outer using block started");
using (Program p2 = new Program(nameof(p2)))
{
Console.WriteLine("Inner using block started");
Console.WriteLine("Inner using block ended");
}
Console.WriteLine("Outer using block ended");
}
Console.WriteLine("Main ended");
}
}
针对.NET Framework 4.7.2构建并运行,您将获得以下输出:
Main started
Initializing the 'p0' instance
Initializing the 'p1' instance
Outer using block started
Initializing the 'p2' instance
Inner using block started
Inner using block ended
Releasing the 'p2' instance's managed resources
Releasing the 'p2' instance's unmanaged resources
Outer using block ended
Releasing the 'p1' instance's managed resources
Releasing the 'p1' instance's unmanaged resources
Main ended
Releasing the 'p0' instance's unmanaged resources
构建并针对.NET Core 2.1运行,您将获得以下输出:
Main started
Initializing the 'p0' instance
Initializing the 'p1' instance
Outer using block started
Initializing the 'p2' instance
Inner using block started
Inner using block ended
Releasing the 'p2' instance's managed resources
Releasing the 'p2' instance's unmanaged resources
Outer using block ended
Releasing the 'p1' instance's managed resources
Releasing the 'p1' instance's unmanaged resources
Main ended
由于p1
语句,实例p2
和using
以与构造它们相反的顺序放置,并且托管和非托管资源均被释放。这是尝试使用“析构函数”背后的预期行为。
另一方面,.NET Framework和.NET Core最后做了一些不同,显示了我一开始提到的区别:
p0
的终结器,因此它仅释放了非托管资源。p0
的终结器。