什么是 .NET 中的析构函数、处置和终结?

作者:微信公众号:【架构师老卢】
6-8 9:10
20

概述:内存管理被认为是困难的,因为它非常抽象。但是,有了正确的指导和正确的方法,这只是您将牢记在心的另一个概念!今天的问题是:.NET 中的 destructor dispose 和 finalize 和有什么不一样?像往常一样,我将提供 C# 语言的 .NET 面试问题的代码示例。话虽如此,将您的战利品放在舒适的位置,让我们开始吧!介绍在编程中,有效地管理资源是相当困难的,也是确保应用程序稳定性和性能的重要部分。在用于处理资源的技术中,在软件面试中经常会遇到三个概念:析构函数、处置和终结。虽然它们都服务于资源清理的目的,但它们的操作方式不同,了解它们的区别是高级开发人员的标志。让我们深入研究这些机

内存管理被认为是困难的,因为它非常抽象。但是,有了正确的指导和正确的方法,这只是您将牢记在心的另一个概念!

今天的问题是:

.NET 中的 destructor dispose 和 finalize 和有什么不一样?

像往常一样,我将提供 C# 语言的 .NET 面试问题的代码示例。

话虽如此,将您的战利品放在舒适的位置,让我们开始吧!

介绍

在编程中,有效地管理资源是相当困难的,也是确保应用程序稳定性和性能的重要部分。

在用于处理资源的技术中,在软件面试中经常会遇到三个概念:析构函数、处置和终结。

虽然它们都服务于资源清理的目的,但它们的操作方式不同,了解它们的区别是高级开发人员的标志。让我们深入研究这些机制中的每一个,并探讨它们在 .NET 框架中的作用。

让我们首先从析构函数开始。

Destructor 🔑

在 .NET 中,a 是在类中定义的特殊成员函数,当垃圾回收器即将销毁或回收对象时,会自动调用该函数。

它的语法类似于构造函数的语法,在类名后加一个波浪号。析构函数主要用于清理非托管资源,例如文件句柄或数据库连接。

class MyClass  
{  
    ~MyClass()  
    {  
        // Cleanup unmanaged resources  
    }  
}

但是,由于析构函数的性质,不建议仅依靠析构函数进行资源清理。垃圾回收器决定何时完成对象,这种缺乏控制可能会导致潜在的资源泄漏或资源管理效率低下。

让我们看看别的东西,终结者!

Finalize

Finalizer 用作资源清理的备份机制,以防未显式调用对象的 Dispose 方法。

在回收对象占用的内存之前,垃圾回收器会自动调用它。但是,不鼓励仅依靠 Finalize 进行资源清理,因为它具有非确定性,并且可能会对性能产生影响。

class MyClass  
{  
    ~MyClass()  
    {  
        // Finalize method implementation  
    }  
}

现在,我是故意这样做的!

你可能在想。

但是伊沃,这些是同样的东西吗?这是怎么回事?

我看看。

Finalize vs Destructor

终结器(以前称为析构函数)用于在垃圾回收器收集类实例时执行任何必要的最终清理。在大多数情况下,可以通过使用 或派生类包装任何非托管句柄来避免编写终结器。SafeHandle

析构函数的代码被隐式转换为以下代码:

protected override void Finalize()  
{  
    try  
    {  
        // Cleanup statements...  
    }  
    finally  
    {  
        base.Finalize();  
    }  
}

让我展示一下 MSDN 中的一些内容。

public class Destroyer  
{  
   public override string ToString() => GetType().Name;  
  
   ~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");  
}

终结器在对象的基类上隐式调用 Finalize。因此,对终结器的调用被隐式转换为以下代码:

protected override void Finalize()  
{  
    try  
    {  
        // Cleanup statements...  
    }  
    finally  
    {  
        base.Finalize();  
    }  
}

你必须记住,即使为GC举起旗帜,你在这里也不是真正的控制权。

程序员无法控制何时调用析构函数,因为这由垃圾回收器确定

因此,换句话说,在 C# 术语中,析构函数和终结器基本上是可互换的概念,并且应该用于在收集类型(例如外部句柄)时释放非托管资源。

您很少需要编写最终版本。

下面是一个代码示例来支持您刚刚阅读的内容。

using System;  
  
class ResourceHandler  
{  
    private IntPtr unmanagedResource; // Example of unmanaged resource  
  
    // Constructor  
    public ResourceHandler()  
    {  
        unmanagedResource = SomeNativeLibrary.AllocateResource();  
    }  
  
    // Destructor (also known as finalizer)  
    ~ResourceHandler()  
    {  
        CleanupResources();  
        Console.WriteLine("Destructor (finalizer) called. Unmanaged resources released.");  
    }  
  
    // Method to clean up resources  
    private void CleanupResources()  
    {  
        // Clean up unmanaged resources  
        SomeNativeLibrary.DeallocateResource(unmanagedResource);  
    }  
  
    // Method to simulate some operation with the resource  
    public void DoSomething()  
    {  
        Console.WriteLine("Doing something with the resource.");  
    }  
  
    // Dispose method  
    public void Dispose()  
    {  
        CleanupResources();  
        GC.SuppressFinalize(this); // Prevent the finalizer from being called  
        Console.WriteLine("Dispose method called. Unmanaged resources released.");  
    }  
}  
  
class Program  
{  
    static void Main(string[] args)  
    {  
        ResourceHandler resource = new ResourceHandler();  
  
        // Use the resource  
        resource.DoSomething();  
  
        // Simulate forgetting to dispose the resource  
        // resource.Dispose(); // Uncomment this line to simulate calling Dispose()  
  
        // Let the object go out of scope and be collected  
        resource = null;  
  
        // Force garbage collection to occur (not recommended in normal usage)  
        GC.Collect();  
        GC.WaitForPendingFinalizers();  
  
        Console.WriteLine("Program ended.");  
    }  
}

Dispose

另一方面,Dispose 方法提供了一种显式释放资源的确定性方法。它通常由利用非托管资源或包含对一次性对象的引用的类实现。

通过实现接口并定义 Dispose 方法,类可以在不再需要资源时立即释放资源,而不是等待垃圾回收。IDisposable

主要问题是 GC 是非确定性的,因此该方法可以支持_确定性_清理。Dispose()

这与垃圾回收无关,并允许调用方_更快地_释放任何资源。它还适用于_托管_资源(除了非托管资源之外),例如,如果您有一个_封装_(例如)数据库连接的类型,您可能也希望释放该类型以释放连接。

class MyClass : IDisposable  
{  
    private bool disposed = false;  
  
    public void Dispose()  
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    }  
  
    protected virtual void Dispose(bool disposing)  
    {  
        if (!disposed)  
        {  
            if (disposing)  
            {  
                // Cleanup managed resources  
            }  
  
            // Cleanup unmanaged resources  
  
            disposed = true;  
        }  
    }  
  
    ~MyClass()  
    {  
        Dispose(false);  
    }  
}

通过显式调用 Dispose 或使用 using 语句,开发人员可以确保及时清理资源,从而提高内存使用效率和应用程序性能。

虽然所有三种机制(destructors, Dispose, and Finalize)都用于 .NET 中的资源清理,但它们的操作方式不同,适用于不同的方案。

析构函数/终结器主要用于释放非托管资源,Dispose 为托管和非托管资源提供确定性清理。

正如你所看到的,在其基本形式中,它们都只是一段代码,将被调用并删除一些句柄/数据,以释放内存。

阅读排行