注意,程序员们!有没有偶然发现 C# 中的 Dispose 和 Finalize 方法,想知道它们是怎么回事?让我们深入了解一下。准备好进入 C# 垃圾回收的兔子洞的激动人心的旅程。
有没有想过 C# 如何处理代码中未使用或“花费”的对象?输入垃圾回收 (GC)!GC 就像您的个人清洁服务一样,通过处理不再使用的物品来清理不需要的内存空间。
通过解开 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
“但是这会如何影响我的应用程序的性能?”你可能会问。不要害怕,我们的摇滚明星程序员!在性能方面,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 岁的孩子熟练地浏览他们的乐高积木藏匿处一样。这不是一场比赛,而是一对能力很强的二人组,发挥他们的优势,这就是一切的不同之处。
由于我们喜欢将大量信息打包到一口大小的工具中,因此让我们通过比较表来总结我们的分析。请记住,每个都有自己的优势,并且在不同的场景中非常有用。