可以采用某些编码技术来确保释放对象。这些技术包括在代码中使用以下内容:
-
Dispose 方法
-
using 语句
-
try 、catch 和 finally 块
Dispose 与 Close 方法的用途
SPWeb 对象和 SPSite 对象的 Dispose 和 Close 方法作用相同。Dispose 方法调用对象的 Close 方法。我们建议调用 Dispose 方法,而不是调用 Close 方法,因为 SPWeb 和 SPSite 对象实现 IDisposable 接口,标准 .NET Framework 垃圾收集调用 Dispose 方法从内存中释放与对象关联的任何资源。
using 语句
通过使用 Microsoft Visual C# 和 Visual Basic using 语句,可以自动释放实现 IDisposable 接口的 SharePoint 对象。
以下代码提供了示例。
String str;
using(SPSite oSPsite = new SPSite("http://www.msiw.net/"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}
利用 using 语句可以大大简化代码。公共语言运行库会将 using 语句转换为 try 和 finally 块,并且会为您释放实现 IDisposable 接口的任何对象。但是,在许多情况下,using 语句并不可取,或者必须慎用并要了解运行库所执行的操作。以下代码示例显示了一个不希望运行库为您构建 finally 块和释放对象的情况。在此情况下,SPContext 返回 SPWeb 对象。
// Do not do this. Dispose() is automatically called on SPWeb.
using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... }
SPContext 对象由 SharePoint 框架进行管理并且不应该在代码中明确释放。SPContext.Site、SPContext.Current.Site、SPContext.Web 和 SPContext.Current.Web 返回的 SPSite 和 SPWeb 对象也是如此。
每当在同一行上合并 SharePoint 对象模型调用时,必须慎重并知道运行库所做的操作。这种情况引发的泄露最难找到。
在以下代码示例中,SPSite 对象会被实例化但不被释放,因为运行库会确保仅释放 OpenWeb 返回的 SPWeb 对象。
void CombiningCallsLeak()
{
using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
{
// ... New SPSite will be leaked.
} // SPWeb object web.Dispose() automatically called.
}
通过将一个 using 语句嵌套在另一个语句中,可以解决此问题。
void CombiningCallsBestPractice()
{
using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// Perform operations on site.
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
如果不对 SPSite 对象执行任何操作,则可以像在以下代码示例中那样更加简洁地编写此内容。
void CombiningCallsBestPractice()
{
using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
using (SPWeb web = siteCollection.OpenWeb())
{
// Perform operations on site.
} // SPWeb object web.Dispose() automatically called; SPSite object
// siteCollection.Dispose() automatically called.
}
在其他情况下,必须构建自己的 try、catch 和 finally 块。需要处理异常并因此必须包括 catch 块的情况就是最明显的示例。
try、catch 和 finally 块
每当需要处理异常时,使用 try、catch 和 finally 块显然是有意义的。try/catch 块中的任何代码都应该具有 finally 控制子句,这样可以确保释放实现 IDisposable 的对象。请注意,在以下代码示例中,应该在 catch 块中填充处理异常的代码。决不要将 catch 块留空。另外请记下在释放之前测试 null 的最佳实践。
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("http://server/");
oSPWeb = oSPSite.OpenWeb(..);
str = oSPWeb.Title;
}
catch(Exception e)
{
// Handle exception, log exception, etc.
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
}
在 foreach 块内创建可释放的对象时,为了避免潜在泄露,必需使用 Try 和 finally 块或 using 语句,如以下代码示例中所示。
public static void SPSiteCollectionForEachBestPractice()
{
string sUrl = "http://www.msiw.net/";
using (SPSite siteCollectionOuter = new SPSite(sUrl))
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
SPSite siteCollectionInner = null;
foreach (siteCollectionInner in siteCollections)
{
try // Should be first statement after foreach.
{
Console.WriteLine(siteCollectionInner.Url);
// Exception occurs here.
}
finally
{
if(siteCollectionInner != null)
siteCollectionInner.Dispose();
}
}
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Response.Redirect 以及 try 块、catch 块、finally 块和 using 语句
在 try 块中调用 Response.Redirect 之后,将会执行 finally 块。Response.Redirect 最终会生成 ThreadAbortException 异常。出现此异常时,运行库会执行所有 finally 块,然后结束线程。但是,因为 finally 块可能会执行无限制的计算或取消 ThreadAbortException,所以该线程未必会结束。因此,必须先释放对象,然后才能重定向或传输处理。如果代码必须重定向,请按照与以下代码示例类似的方法来实现代码。
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("http://www.msiw.netserver/");
oSPWeb = oSPSite.OpenWeb(..);
str = oSPWeb.Title;
if(bDoRedirection)
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
Response.Redirect("newpage.aspx");
}
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
}
因为 using 语句会命令运行库创建 finally 块,所以每当在 using 语句内使用 Response.Redirect 时,请确保正确释放对象。以下代码示例演示如何执行此操作。
using (SPSite oSPSite = new SPSite("http://server/"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
if (bDoRedirection)
Response.Redirect("newpage.aspx");
}
缩短长时间对象保留期的建议
通过遵循以下常规建议,可以缩短 SharePoint 对象的长时间保留期。
-
如果用 new 运算符创建对象,请确保用于创建的应用程序释放该对象。
良好的编码实践 #1
明确释放
void CreatingSPSiteExplicitDisposeNoLeak()
{
SPSite siteCollection = null;
try
{
siteCollection = new SPSite("http://www.msiw.net/");
}
finally
{
if (siteCollection != null)
siteCollection.Dispose();
}
}
良好的编码实践 #2
自动释放
CreatingSPSiteWithAutomaticDisposeNoLeak()
{
using (SPSite siteCollection = new SPSite("http://www.msiw.net/"))
{
} // SPSite object siteCollection.Dispose() is called automatically.
}
释放由返回其他 SPWeb 对象的 SharePoint 方法(如 OpenWeb())所创建的项。
良好的编码实践
void OpenWebNoLeak()
{
using (SPSite siteCollection = new SPSite("http://www.msiw.net/"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
不要在线程之间共享任何 SPRequest 对象(以及通过扩展共享任何包含对 SPRequest 对象的引用的对象)。不支持以下性质的任何编码技术:在两个或更多个线程之间共享 SPRequest 对象,或者在一个线程上创建 SPRequest 对象而在另一个线程上释放该对象。这意味着,无法在静态变量中存储任何包含对 SPRequest 对象的引用的对象。因此,不要将实现 IDisposable 的 SharePoint 对象(例如 SPWeb 或 SPSite)存储在静态变量中。