是否存在使用不会丢弃物体的情况?

时间:2012-02-22 22:19:21

标签: c# using-statement

是否有任何情况下使用不会丢弃它应该处理的对象?

例如,

using(dbContext db = new dbContext()){ ... }

有没有办法在最后一个} db之后仍然存在?

如果出现这种情况该怎么办:

object o =  new object();
using(dbContext db = new dbContext()){
 o = db.objects.find(1);
}

o是否可以让db保持活着?

10 个答案:

答案 0 :(得分:6)

我认为你混淆了两个概念:处理和垃圾收集。

处理对象会释放此对象使用的资源,但这并不意味着该对象已被垃圾回收。垃圾收集只会在不再引用您的对象的情况下发生。

因此,在您的示例中,将在db.Dispose块的末尾调用using(这将关闭连接),但DbContext仍会被{{1}引用}}。由于o是一个局部变量,因此当方法返回时,o将有资格进行垃圾收集。

答案 1 :(得分:5)

Alive Disposed 是两回事。

如果o是一个对象,它将引用(通过字段,属性,本身等)保存到dbContext对象,并且垃圾回收器确定两个{{1}并且仍然可以访问dbContext对象,然后该对象将保持活动状态。但是,一旦执行正常退出使用块,该对象仍将被处理。

答案 2 :(得分:4)

db将会一直存在,直到它超出范围,对它的所有引用都将丢失,并且GC决定进入。调用Dispose不会清除对象本身,它告诉对象清理它所持有的任何本机资源。

IDisposable接口完全是关于管理GC无法管理的本机资源。对象本身是由GC处理的托管对象。在您的示例中,此对象可能维护与作为本机资源的数据库的连接。

GC无法清除它,所以为了确定性地管理这个内存/资源,类实现IDisposable告诉客户“嘿,你需要做一些额外的工作来确保我需要的资源以尽可能有效的方式照顾我的工作。“

顺便说一句,IDisposable的正确实现将释放终结器中的所有本机资源,因此如果您未能调用Dispose,则不应遇到内存泄漏。但是,这是非确定性的,您可能会通过这样做很好地遇到问题。良好的做法要求您尽快释放这些资源,这就是您自己致电Dispose的原因。

答案 3 :(得分:2)

在使用块结束后,从GC的角度来看,对象可能是 alive ,但它将被处理掉。在你的第二个例子中,如果db.objects.find(1)返回了对db的引用(确实很奇怪),你会有这样的事情:

object o;
using (dbContext db = new dbContext())
{
    o = db;
}

// at this point in the code, the object is disposed,
// but referenced by the variable o; the object is
// therefore not yet eligible for garbage collection.

答案 4 :(得分:2)

  

o有可能让db保持活着吗?

在不被收集的意义上活着:是的。

但是using() {}块意味着Dispose(),这意味着Close(),因此您将无法再将其用于任何有意义的事情。

答案 5 :(得分:1)

using保证当执行移出IDisposable.Dispose块时,将在目标对象上调用using。无论如何,这都会发生。

目前还不清楚你的意思是o可能会db保持活着 - db会被处理掉,所以如果你试图使用它,你可能会获得ObjectDisposedException然后。这算是“死了”吗?如果是,那就死了,吉姆。

答案 6 :(得分:1)

将对象设置为特定实例不会使数据库连接保持活动状态。现在,从技术上讲,它并没有真正处理,因为有一个池来保持这些昂贵的对象存活,但从你的观点来看,它的工作方式与处理对象相同,并且它会调用Dispose()方法。

如果您实际设置o = db,那将是一个不同的故事,但您正在处理结果集。

新:还有一件事。根据Dispose()的实际实现方式,如果无法处理对象,您可能会看到异常。

顺便说一句,使用语句基本上与此相同(如果你怀疑的话,编译两者并用反射器检查):

try
{
   var myObject = new MyObject();
}
finally
{
   myObject.Dispose();
}

答案 7 :(得分:1)

using构造等同于try-finally块:

dbContext db = new dbContext();
try
{
   o = db.objects.find(1);
}
finally
{
   db.Dispose();
}

在大多数正常程序执行的情况下,将调用db.Dispose()。但是,这并不意味着db仍然不是内存中的有效对象。正如Ed在他的回答中所说,Dispose()实际上并没有从堆中删除对象并释放内存。只有当变量db当前引用的对象丢失所有引用(因为它们已经超出范围或被重新分配)并且GC将其删除时,才会发生这种情况。

然而,处置对象通常对于在存储器中保持不是很有用;在对象超出范围并进行GCed之前,Dispose()通常是道路的终点。因此,虽然你可以通过创建或分配对另一个变量的引用来保持数据库,但是由于对象已关闭其连接,释放了诸如COM引用或套接字之类的非托管资源,并且通常准备将其删除,因此可能会导致错误从内存中调用Dispose()。

答案 8 :(得分:1)

不,db将永远被处置。

不要混淆处理对象和GC收集的对象的概念。 Disposing是一种释放对象使用的资源的工具,但您始终可以对已处置的对象保持实时引用,以防止对象被收集。一个不排除另一个。

Dispose模式只是确定性地释放资源的一种方式,即使GC没有收集对象(将在没有实时引用时执行) GC决定收集事先无法知道的对象,除非您明确调用GC.Collect)。

答案 9 :(得分:1)

为所有回复添加一个观点(“处置”与“收集垃圾”)。

某些对象具有内部缓存资源,这些资源部分由IDisposable模式管理。即您可以创建一个管理特定文件访问权限的类型。每个实例都将读取/写入文件的一部分,但是处理多个实例中的一个实例将不会关闭文件,直到与文件关联的最后一个实例被处理掉。

由于“使用”不做任何神奇的事情,它将遵循对象在“关闭资源”部分的合同,但它可能不一定意味着“特殊资源现在免费”。因此,在使用对象绝对是“处置”之后,从GC的角度来看可能是“活着的”,并且对象管理的实际资源可能会或可能不会被释放(即从内存/本机句柄中卸载已关闭)。