我对CLR和GC的工作方式很着迷(我正在通过C#阅读CLR、Jon Skeet的书/帖子等来扩展我在这方面的知识).

不管怎么说,说:

MyClass myclass = new MyClass();
myclass = null;

或者,让MyClass实现IDisposable和析构函数并调用Dispose()?

此外,如果我有一个带有using语句的代码块(如下面),如果我单步执行代码并退出using块,那么该对象是被处理的还是在垃圾收集发生时被处理的?如果我在using块中调用Dispose(),会发生什么?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

流类(比如BinaryWriter)有Finalize方法吗?我为什么要用这个?

推荐答案

把垃圾处理和垃圾收集分开是很重要的.它们是完全不同的东西,有一点是相同的,我马上就要谈到.

100, garbage collection and finalization

编写using语句时,它只是try/finally块的语法糖,因此即使using语句体中的代码抛出异常,也会调用Dispose.这意味着对象在块的末尾被垃圾收集.

处置量约为unmanaged resources(非内存资源).这些可能是UI句柄、网络连接、文件句柄等.这些资源有限,所以您通常希望尽快释放它们.当您的类型"拥有"非托管资源时,您应该实现IDisposable,直接(通常通过IntPtr)或间接(例如通过StreamSqlConnection等).

垃圾收集本身只与内存有关-只有一点小问题.垃圾回收器能够找到不能再引用的对象,并将其释放.不过,它并不总是查找垃圾-只有当它检测到需要查找垃圾时(例如,如果某个"一代"堆耗尽了内存).

扭转率是finalization.垃圾收集器保存一个对象列表,这些对象不再可达,但是有终结器(在C#中写为~Foo(),有点令人困惑-它们与C++析构函数完全不同).它在这些对象上运行终结器,以防它们在释放内存之前需要执行额外的清理.

如果类型的用户忘记了有序地处理资源,那么终结器几乎总是用来清理资源.因此,如果你打开一个FileStream,但忘记调用DisposeClose,终结器将eventually为你释放底层文件句柄.在我看来,在一个写得很好的程序中,终结器几乎不应该开火.

Setting a variable to 100

关于将变量设置为null的一个小问题——为了进行垃圾收集,几乎不需要这样做.如果它是一个成员变量,您有时可能会想这样做,尽管根据我的经验,很少有对象的"部分"不再需要.当它是一个局部变量时,JIT通常足够聪明(在发布模式下),可以知道何时不再使用引用.例如:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

唯一值得将局部变量设置为null的地方是当你在一个循环中,循环的一些分支需要使用该变量,但你知道你已经达到了一个不需要的点.例如:

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

Implementing IDisposable/finalizers

那么,您自己的类型是否应该实现终结器呢?几乎可以肯定不会.如果您只有indirectly个持有非托管资源(例如,您有一个FileStream作为成员变量),那么添加您自己的终结器也无济于事:当您的对象符合垃圾回收条件时,几乎可以肯定流有资格进行垃圾回收,所以您可以只依赖FileStream具有终结器(如果需要-它可以引用其他东西,等等).如果您想"几乎"直接持有非托管资源,SafeHandle是您的朋友--这需要一点时间才能上手,但这意味着您将拥有almostnever need to write a finalizer again.您通常只需要一个终结器,如果您对资源(ANIntPtr)有一个真正直接的句柄,并且您应该尽可能快地迁移到SafeHandle.(这里有两个链接-最好是两个都读.)

乔·达菲(Joe Duffy)有一本very long set of guidelines around finalizers and IDisposable页(与许多聪明人合著)值得一读.值得注意的是,如果你密封了你的类,它会让生活变得更加简单:重写Dispose来调用一个新的虚拟Dispose(bool)方法等的模式只有在你的类是为继承而设计的时候才相关.

这有点漫无边际,但请在你想要的地方要求澄清:)

.net相关问答推荐

如何在AutoMapper中添加自定义方法到项目中?

.NET Async / Await:状态机如何知道何时继续执行?

如何在 .net MAUI 中删除不需要编译的平台?

部署时如何控制红隼端口?

问:在 Blazor WASM 应用程序中存储 api 密钥的最佳方式是什么?

AsyncLocal 的语义与逻辑调用上下文有何不同?

ASP.NET MVC:隐藏字段值不会使用 HtmlHelper.Hidden 呈现

使用 .NET 中的代码更改桌面墙纸

如果需要,将方案添加到 URL

日期时间是什么意思?在 C# 中是什么意思?

软件包版本始终为 1.0.0,带有 dotnet pack

如何异步 Files.ReadAllLines 并等待结果?

获取 .NET 程序集的 AssemblyInformationalVersion 值?

强制 XmlSerializer 将 DateTime 序列化为 'YYYY-MM-DD hh:mm:ss'

在关闭警告中访问 foreach 变量

从 'System.Int32' 到 'System.Nullable`1[[System.Int32, mscorlib]] 的无效转换

如何向 C# Winforms 中的标签添加提示或工具提示?

为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?

在 Windows 窗体 C# 应用程序中拥有配置文件的最简单方法

在 C# 中转义无效的 XML 字符