在 C# 中资源回收,你知道要使用哪个吗?

作者:微信公众号:【架构师老卢】
2-16 8:51
125

注意,程序员们!有没有偶然发现 C# 中的 Dispose 和 Finalize 方法,想知道它们是怎么回事?让我们深入了解一下。准备好进入 C# 垃圾回收的兔子洞的激动人心的旅程。

有没有想过 C# 如何处理代码中未使用或“花费”的对象?输入垃圾回收 (GC)!GC 就像您的个人清洁服务一样,通过处理不再使用的物品来清理不需要的内存空间。

C# 语言中的 Dispose 方法#

通过解开 Dispose 方法来启动它。Dispose 在 C# 中实现“IDisposable”接口是一种预定义的方法,旨在提高内存管理效率。

Dispose 方法有一个关键目的:释放垃圾回收器无法自行处理的非托管资源。简单地说,它就像一个记忆解放器,解放了“被困住”的记忆空间!

让我们看一个例子:

public class DisposeExample : IDisposable  
{  
    bool disposed = false;  
  
// Dummy unmanaged resource  
    private IntPtr unmanagedResource;  
    // Implement dispose.  
    public void Dispose()  
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    }  
    protected virtual void Dispose(bool disposing)  
    {  
        if (!disposed)  
        {  
            if (disposing)  
            {  
                /* Here, dispose managed resources. */  
            }  
            // Dispose unmanaged resources.  
            CloseHandle(unmanagedResource);  
            disposed = true;   
        }  
    }  
    ~DisposeExample()  
    {  
        // Simply call Dispose(false).  
        Dispose(false);  
    }  
    [DllImport("Kernel32")]  
    private extern static Boolean CloseHandle(IntPtr handle);  
}

在此示例中,该类实现接口。我们使用该方法来释放资源,并确保 GC 以后不会尝试最终确定对象。DisposeExampleIDisposableDispose()GC.SuppressFinalize(this)

处置方法的实际用例

想知道 Dispose 适合您时尚的 C# 编码工作吗?在处理系统资源处理对象时,通常会执行步骤。想想流、文件、网络连接,甚至是复杂的图形。艺术家,认识你的清理人员!

现在,让我们扩展一些涉及 Dispose 方法的实际场景。

  • 文件处理

// Implement IDisposable.

public class FileHandler : IDisposable  
{  
    // Declare your Filestream object.  
    private FileStream fileStream = null;  
  
public FileHandler(string path)  
    {  
        fileStream = new FileStream(path, FileMode.Open);  
    }  
    public void Dispose()  
    {  
        // Dispose off the Filestream object once it's no longer needed.  
        fileStream?.Dispose();  
    }  
}

在本例中,我们正在处理一个对象。完成读取或写入文件后,我们调用该方法来释放对象。让我们的记忆保持整洁!FileStreamDispose()

  • 数据库连接
public class DatabaseConnector : IDisposable  
{  
    // Declare your SqlConnection object.  
    private SqlConnection sqlConnection = null;  
  
public DatabaseConnector(string connectionString)  
    {  
        sqlConnection = new SqlConnection(connectionString);  
        sqlConnection.Open();  
    }  
    public void Dispose()  
    {  
        // Dispose off the SqlConnection once it's no longer needed.  
        sqlConnection?.Close();  
    }  
}

在 C# 中处理数据库时,Dispose 会更加灵活。在此方案中,我们正在清除一个对象。一旦我们完成数据获取,调用就会确保连接不会不必要地徘徊。SqlConnectionDispose()

  • 图形和图像处理
public class ImageProcessor: IDisposable  
{  
    Image image;  
  
public ImageProcessor(string path)  
    {  
        image = Image.FromFile(path);  
    }  
    // Some processing done here.  
    public void Dispose()  
    {  
        image?.Dispose();  
    }  
}

进入图形数据领域,我们经常使用复杂的对象,这些对象可能会贪婪内存。在这种情况下,该方法有助于在 Image 对象达到其目的后清除它们。Dispose

在我们的 C# 探索路径中,我们遇到的下一个方法是 Finalize 方法,它是 Dispose 的较早且值得信赖的同级方法。Finalize 被标记为针对尚未清理的内存空间的最后一道防线,可以像值得信赖的旧真空吸尘器一样可靠,直到每一寸都被清理干净才停止!让我们做好准备,深入研究 Finalize 方法的复杂性和细微差别及其作为“析构函数”的作用。

发展理解:最终确定方法基础知识

在看到 Finalize 方法的实际应用之前,让我们先对其进行一些分解。简单来说,Finalize 方法是 Microsoft 类提供的析构函数,该方法有助于在函数派对后整理混乱,特别关注清理非托管资源。Object

请看以下示例:

class FinalizeExample  
{  
    // Unmanaged resource  
    IntPtr nativeResource;  
  
~FinalizeExample()  // destructor  
    {  
        // Release the allocated unmanaged resource  
        Marshal.FreeHGlobal(nativeResource);  
    }  
}

在此类中,我们有一个 非托管资源 。标有波浪号 (~) 的析构函数在完成期间释放此非托管资源。FinalizeExamplenativeResource

这是另一种情况:

class FinalizeExample  
{  
    FileStream fileStream;  
  
// Constructor, opens a file named "temp.dat"  
    public FinalizeExample()  
    {  
        fileStream = new FileStream("temp.dat", FileMode.Create);  
    }  
    ~FinalizeExample()  // destructor  
    {  
        // File closing statement  
        fileStream.Close();  
    }  
}

在此代码段中,Finalize 方法关闭对象终结期间,确保释放文件使用的任何系统资源。fileStream

C# 中的 Finalize 方法:性能

“但是这会如何影响我的应用程序的性能?”你可能会问。不要害怕,我们的摇滚明星程序员!在性能方面,Finalize 方法就像一个勤奋的看门人,随时待命以清理任何废弃的内存资源。

有什么好处?优化后端性能,缩短响应时间,从而让用户满意。看到连接了吗?更快的性能意味着用户满意!

但请记住,这里有一个权衡。垃圾回收器需要进行两次传递才能使用终结器清理对象,这可能会导致额外的开销。

现实世界编码中的 Finalize 方法

现在,让我们继续一些实际的东西。在我惊人的代码中,我应该在哪里实现这个 Finalize 方法?它通常与使用非托管资源的类一起发挥作用。很有趣,不是吗?

考虑使用大量 Bitmap 对象的 .NET 应用程序。这些 Bitmap 对象封装了 GDI+,这些 GDI+ 是非托管资源。若要在不再使用这些资源后立即释放这些资源,最好调用 Dispose 方法。但是,在未调用 Dispose 的情况下,析构函数或 Finalize 方法将发挥作用以释放这些 GDI+ 对象。

需要了解的关键是,Finalize 方法并非适用于所有类型的脚本,但在处理非托管资源时更是如此。就像即使是最好的派对有时也会让乐队难忘一样,只要使用得当,时机和时机,Finalize 可能会成为您代码的摇滚明星!

处置与最终确定

女士们,先生们,请戴上代码帽,因为我们即将深入研究一个鲜为人知的领域:C# 中 Dispose 和 Finalize 方法之间的战斗。系好安全带,因为我们将深入研究这两种方法如何相互衡量。

性能:处置与最终确定

“性能为王,”你说?那么处置可能会赢得你的心。调用 Dispose 方法时,会立即释放托管和非托管资源。这增强了代码的性能,因为它将资源从进一步使用中解放出来,例如让您的计算机打个盹,以便它准备好承担下一个任务。

但不要让任何人告诉你 Finalize 是一个慢动作!当然,它是自动的,可以在 GC 过程中工作,但它专注于 Dispose 无法触及的资源。这就像一个超级英雄在其他一切都失败时介入并拯救。

为了清楚起见,让我们深入了解一些代码。请考虑以下场景:代码中有一个对象,用于打开和读取文件,我们将其称为 FileReader:

public class FileReader: IDisposable  
{  
    bool disposed = false;  
    StreamReader file;  
  
public FileReader(string fileName)  
    {  
        file = new StreamReader(fileName);  
    }  
    public void ReadFile()  
    {  
        /* Code to read the file */  
    }  
    public void Dispose()  
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    }  
    protected virtual void Dispose(bool disposing)  
    {  
        if(!disposed)  
        {  
            if (disposing)  
            {  
                file?.Close();  
            }  
            disposed = true;   
        }  
    }  
    ~FileReader()  
    {  
        Dispose(false);  
    }  
}

在此示例中,Dispose 将在我们读取它后立即关闭它,从而使内存管理非常高效。file

另一方面,Finalize 会延迟扫描,以确保最终处理所有未清理的内容。这就像有一个房子清洁工,在今天打扫完你的房子后,下周突然出现,只是为了仔细检查一切是否井井有条!

处置与最终确定用例的示例

让我们让它更具体一点。将 Dispose 想象成泄漏巡逻的海绵。你有没有洒过东西,立即把它吸收起来,然后开始你的一天?这就是在工作中处置!它巡逻,找到泄漏物(未使用的资源),并立即清理它。在代码方面,一旦对象完成其目的,Dispose 就非常适合使用。

public void ProcessFile(string fileName)  
{  
    using (var reader = new FileReader(fileName))  
    {  
        reader.ReadFile();  
    } // Dispose is called here.   
}

在上面的代码中,我们使用了一旦完成工作就会自动调用 Dispose 的语句。usingreader

另一方面,Finalize 就像项目结束后的清理工作。你知道当你粉刷完你的房间时,退后一步欣赏你的作品,然后注意到一些你错过的油漆飞溅吗?这时 Finalize 就进来了!它会在您的代码中漫游,并清除 Dispose 忽略的挥之不去的、不受管理的小斑点。

public class ProcessFile  
{  
    FileReader reader;  
  
public ProcessFile(string fileName)  
    {  
        reader = new FileReader(fileName);  
    }  
    public void ReadFile()  
    {  
        reader.ReadFile();  
        // FileReader destructor will be called sometime after this line, by the GC  
    }  
}

在此示例中,当对象标记为 GC 时,Finalize(析构函数)将确保 的文件已关闭。ProcessFilereader

因此,无论您选择哪种方式,处置或完成,您的代码都会得到妥善处理,就像一个 8 岁的孩子熟练地浏览他们的乐高积木藏匿处一样。这不是一场比赛,而是一对能力很强的二人组,发挥他们的优势,这就是一切的不同之处。

处置与最终确定比较

由于我们喜欢将大量信息打包到一口大小的工具中,因此让我们通过比较表来总结我们的分析。请记住,每个都有自己的优势,并且在不同的场景中非常有用。

相关留言评论
昵称:
邮箱:
阅读排行