繁体   English   中英

在语句中间创建的IDisposable对象会发生什么,我无法明确地调用Dispose()?

[英]What happens to an IDisposable object created in the middle of a statement that I cannot explicity call Dispose() on?

假设我正在使用Sharepoint(这也适用于其他对象模型)并且在我的语句中间,我调用了一个方法,在本例中为“OpenWeb()”,它创建了一个IDisposable SPWeb对象。 现在,我无法在SPWeb对象上调用Dispose(),因为我没有对它的引用。 那么我需要关注这个泄漏的记忆吗?

SPUser spUser = SPControl.GetContextSite(HttpContext.Current).OpenWeb().SiteUsers[@"foo\bar"];

我知道我可以把语句拆分成多行并获取SPWeb引用来调用Dispose:

SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb();
SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
spWeb.Dispose();

请记住,我的问题不是关于美学,而是关于IDisposable对象发生了什么,我无法明确地调用Dispose(),因为我没有引用。

很抱歉在我第一次提出问题时不够清楚。 我已经改写了它。 感谢到目前为止的所有回复。

“IDisposable对象会发生什么,我无法显式调用Dispose()?”

通常,您可以在所有一次性对象上调用Dispose(隐式使用using语句或显式),但是在您不能的假设场景中,它取决于对象的实现方式。

通常,.Net对象将沿着这些线遵循模式。 该模式是定义一个终结器,在没有调用dispose的情况下清除东西,然后使用dispose来抑制终结器。 这减少了内存负载,并减少了GC的工作量。

从终结器中调用Dispose的许多问题之一是,您将单线程问题转变为多线程问题,终结器将在不同的线程上运行,这可能会暴露一些非常微妙且难以捕获的错误。 此外,这意味着您将持续超过预期的非托管资源(例如,您打开文件,忘记致电关闭或处置,下次再打开它锁定)

重要的是,处理所有一次性物品是最佳做法 ,否则你可能会引入奇怪而复杂的错误。 需要注意的是,某些框架(如sharepoint)会返回不应根据文档处理的对象的共享实例。

当我使用“using”模式处理对象时,我通常会发现代码更具可读性。 显式调用Dispose(object.Dispose())的问题在于,很难跟踪对象的分配位置,并且很容易忘记。 你不能忘记关闭using语句的大括号,编译器会抱怨:)

编辑/ GOTCHA

根据MS文档,您不应该对GetContextSite返回的共享点共享对象的引用调用dispose。 所以,你应该格外小心。

有关您应该使用的安全共享点模式,请参阅此答案

但是,如果您具有对共享资源的引用,例如,当Web部件中的GetContextSite方法提供该对象时,请不要使用任何一种方法来关闭该对象。 在共享资源上使用任一方法都会导致发生访问冲突错误。 在您引用共享资源的情况下,请让Windows SharePoint Services或门户网站应用程序管理该对象。

以下是更惯用的,并且读起来也更好:

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}

您应该显式(或通过using语句隐式)调用Dispose方法。 将代码分成多行的另一个原因是:

  • 可读性
  • 它更容易调试

Dispose方法可以在终结器中执行,但自己调用它会更安全。

我建议拆分线并使用Dispose。 如果一个对象实现了IDisposable,你必须假设它需要处理,因此在using块中使用它。

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SpUser spUser = null;
    if (spWeb != null)
    {
        spUser = spWeb.SiteUsers[@"foo\bar"];
    }
}

通过这样做,您可以处理Dispose对象并处理打开外部资源的OpenWeb()调用中的错误。

泄漏记忆? 不,你不应该担心它,假设IDisposable的实现符合类库指南,因为下一个垃圾收集应该清理它。

但是,它确实显示了代码中的错误,因为您没有正确管理与您合作的IDisposable实现的生命周期(我在http://www.caspershouse.com/post/A-上详细说明了这个问题) 更好的实现 - 模式为IDisposable.aspx )。 您的第二个代码块是一个很好的第一步,但如果对SiteUsers的调用失败,则不保证调用Dispose。

您修改后的代码如下所示:

// Get the site.
var contextSite = SPControl.GetContextSite(HttpContext.Current);

// Work with the web.
using (SPWeb web = contextSite.OpenWeb())
{
  // Get the user and work with it.
  SPUser spUser = web.SiteUsers[@"foo\bar"];
}

正如一些人到目前为止所说的那样:你绝不能丢弃你不创造的物体。 因此,在你的榜样,你应该处理要么SPWeb或SPSite对象!

这将破坏当前的SPRequest对象。 它可能看起来仍然有效,但如果您稍后添加新的Web部件,或尝试打开W​​eb部件的工具窗格,您将收到各种奇怪的错误。

正如已经说过的那样,必须处理自己创建的SPWeb和SPSite实例(新)。

这可以使用using()或try / finally(无论如何这都是using()语句显示在你的MSIL代码中的方式)。 如果您使用try / finally,最好在SPWeb / SPSite实例上检查null,并首先检查SPWeb,因为SPSite会自动处理您的SPWeb。

另一个要记住的重要事项是,循环SPWebCollections(如AllWebs或Webs)是在循环遍历它们时处置子网站。 如果有很多子网,并且您在具有有限内存潜力的32位硬件上运行,则可以非常快速地用SPRequest对象填充内存。 这将导致性能低下,因为它会导致应用程序池定期回收。

说到这一点,最好不要像在代码示例中那样组合调用。 这很难理解, 如果你正在处理一个你应该处理的SPWeb,你就不可能! 这些内存泄漏是最难发现的,所以不要这样做;-)

我可以推荐Roger Lamb的博客了解详情: http//blogs.msdn.com/rogerla还有一些关于StefanGoßner博客的技术细节: http//blogs.technet.com/stefan_gossner/archive/2008/12/05/disposing -spweb和-SPSite的-objects.aspx

安德斯

这里的许多答案都假设最重要的是只调用Dispose。 但是,在使用SPSite和SPWeb时,您肯定希望尽快调用Dispose()。 确定何时应该通常很棘手,但有很多好的参考资料可以帮助回答这个问题。

至于为什么会这样,StefanGoßner 在这里提供了一个很好的总结:

每个SPWeb和SPSite对象都包含对SPRequest对象的引用,该对象包含对负责与后端SQL Server通信的SharePoint COM对象的引用。

处置SPWeb对象实际上不会从内存中删除SPWeb对象(实际上.NET框架不允许以确定的方式从内存中删除任何对象)但它将调用SPWeb对象的方法,该方法导致COM对象关闭连接到SQL服务器并释放其分配的内存。

这意味着从创建SPRequest对象到放置SPWeb对象的那一刻,到后端SQL服务器的连接将保持打开状态。

代码示例的最佳实践如下所示:

SPSite contextSite = SPControl.GetContextSite(HttpContext.Current);
using (SPWeb spWeb = contextSite.OpenWeb())
{
  SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
  // Process spUser
}
// DO NOT use spUser
// DO NOT dispose site from HttpContext

请注意,在放置父SPWeb之后使用SPUser,SPList等SP对象是不安全的。

来自http://msdn.microsoft.com/en-us/library/aa973248.aspx 最佳实践:使用一次性Windows SharePoint Services对象

如果从SPControl.GetContextSite获取SPSite对象,则调用应用程序不应该丢弃该对象。 由于SPWeb和SPSite对象保留以这种方式派生的内部列表,因此处置该对象可能会导致SharePoint对象模型无法预测。

似乎没有人抛出这个:

如果你的对象需要一个处理器(即占用必须释放的资源),那么你应该实现一个调用处理器方法的终结器。

在处理器中,您可以添加以下行:

System.GC.SuppressFinalize(this)

如果您致电处理器,这将阻止最终确定。 这样你就可以在必要时很好地使用你的对象,但保证它通过终结器自行清理(这是C#确实有终结器的全部原因)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM