我最近与同事讨论了Dispose实施的价值和类型IDisposable.
我认为即使没有非托管资源可以清理IDisposable,也应该尽快清理类型.
我的同事有不同的想法; 执行IDisposable,如果你没有任何非托管资源为你的类型,最终会被垃圾收集是没有必要的.
我的论点是,如果你有一个想要尽快关闭的ADO.NET连接,那么实现IDisposable并且using new MyThingWithAConnection()有意义.我的同事回答说,在封面下,ADO.NET连接是一种非托管资源.我对他回复的答复是,最终所有东西都是一种非托管资源.
我知道推荐的一次性模式,如果Dispose被调用,你可以免费使用托管和非托管资源,但是如果通过终结器/析构函数调用,则只有免费的非托管资源(前面有关于如何提醒消费者不正确使用您的IDisposable类型的博客)
所以,我的问题是,如果你有一个不包含非托管资源的类型,是否值得实现IDisposable?
我认为如果您的程序没有,GC会最终调用Dispose,但是您应该在程序中调用Dispose()以使清除确定性.
但是,从我的小测试程序来看,我根本没有看到Dispose被调用....
public class Test : IDisposable
{
static void Main(string[] args)
{
Test s = new Test();
s = null;
GC.Collect();
Console.ReadLine();
}
public Test()
{
Console.WriteLine("Constructor");
}
public void Dispose()
{
Console.WriteLine("Dispose");
}
}
Run Code Online (Sandbox Code Playgroud)
//输出只是"构造函数",我没有像我期望的那样得到"Dispose".这是怎么回事?
编辑:是的,我知道我应该调用Dispose() - 我在使用一次性物体时遵循标准模式.我的问题出现了,因为我试图追踪某些elses代码的漏洞,这是托管C++(另一层复杂性,这将是另一个线程的好主题).
我无法相信我仍然对此感到困惑但是,无论如何,让我们最终指出:
我有一个类覆盖OnPaint来做一些绘图.为了加快速度,我在构造函数中预先创建了笔,画笔等,以便OnPaint不需要继续创建和处理它们.
现在,我确保我总是处理这些对象,但我感觉我不需要,因为尽管它们实现了IDisposable,但它们都是托管对象.
它是否正确?
感谢所有的答案,这个问题肯定已被钉上了.
我很高兴我一直保持警惕,总是使用'使用',所以我不需要经历所有的代码检查.我只想清楚我不是一个毫无意义的用户.
顺便说一句,我确实有一个奇怪的情况,最近,我不得不更换一个使用块并手动调用dispose!我会把它挖出来并创造一个新问题.
我已经告诉System.IO.MemoryStream不必在被包装使用的块,因为没有基本的资源,这有点违背我一直都告诉流("如有疑问,请使用使用 ") .
这是真的?为什么MSDN示例使用一个(总结如下)?
using(MemoryStream memStream = new MemoryStream(100))
{
// do stuff
}
Run Code Online (Sandbox Code Playgroud) 可以创建大量内存密集型对象,然后放弃对它们的引用.例如,我可能想要从数据库下载和操作一些数据,我将进行100次单独的下载和处理迭代.我可以声明一次DataTable变量,并且对于每个查询,使用构造函数将其重置为新的DataTable对象,从而放弃内存中的旧DataTable对象.
DataTable类具有简单的内置方式来释放它使用的内存,包括Rows.Clear()和.Dispose().因此,在将变量设置为新的DataTable对象之前,我可以在每次迭代结束时执行此操作.或者我可以忘掉它,让CLR垃圾收集器为我做这件事.垃圾收集器似乎非常有效,因此最终结果应该是相同的.当你不需要它们时,显然处理内存繁重的对象(但是添加代码来执行此操作)或者只是依靠垃圾收集器为你做所有的工作(你受到了摆布GC算法,但你的代码更小)?
根据要求,这里是代码说明了回收的DataTable变量示例:
// queryList is list of 100 SELECT queries generated somewhere else.
// Each of them returns a million rows with 10 columns.
List<string> queryList = GetQueries(@"\\someserver\bunch-o-queries.txt");
DataTable workingTable;
using (OdbcConnection con = new OdbcConnection("a connection string")) {
using (OdbcDataAdapter adpt = new OdbcDataAdapter("", con)) {
foreach (string sql in queryList) {
workingTable = new DataTable(); // A new table is created. Previous one is abandoned
adpt.SelectCommand.CommandText = sql;
adpt.Fill(workingTable);
CalcRankingInfo(workingTable);
PushResultsToAnotherDatabase(workingTable);
// Here I could call workingTable.Dispose() …Run Code Online (Sandbox Code Playgroud) 析构函数应该只释放对象所持有的非托管资源,并且不应该引用其他对象.如果您只有托管引用,则不需要(也不应该)实现析构函数.您希望这仅用于处理非托管资源.因为拥有析构函数需要一些成本,所以你应该只在消耗有价值的非托管资源的方法上实现它.
文章没有深入讨论这个问题,但在C#中使用析构函数会涉及哪些成本?
注意:我知道GC以及在可靠的时候没有调用析构函数的事实,除此之外,还有什么吗?
我新客户所在的代码审查清单如下 -
实现Dispose和Finalize的类应该在Dispose实现中调用GC.SupressFinalize
为什么?
它是否应该读取为实现IDisposable接口的类应该在Dispose实现中调用GC.SupressFinalize?
或者我错过了一些愚蠢的东西?
.net garbage-collection idisposable finalizer suppressfinalize
使用GDI +进行编程时,我是否需要坚持使用模式来处理各种对象,如Brush,Font,这会使代码变得非常混乱.
有什么建议?
.net ×6
c# ×5
idisposable ×4
finalizer ×3
destructor ×1
dispose ×1
finalization ×1
gdi ×1
gdi+ ×1
memory ×1
stream ×1