我正在使用.net,需要获取一些html文本,所以我想我应该一起使用HtmlTextWriter和StringWriter来获取格式良好的html。但是,尽管我用各种不同的方式编写代码,我仍然会收到来自静态代码分析器的警告(使用Microsoft all Rules)。在下面的代码示例中,我在注释中显示了代码分析器警告。为了简化代码,我实际上没有对HtmlTextWriter进行任何调用(您将在每个函数中看到相应的注释)。如何正确编写代码以避免出现警告?
// CA2000 : Microsoft.Reliability : In method 'Default.Func1()', object 'stringWriter' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'stringWriter' before all references to it are out of scope.
public static string Func1()
{
string html;
StringWriter stringWriter;
using (var writer = new HtmlTextWriter(stringWriter = new StringWriter()))
{
// You would do some stuff with the writer here, but not for this example.
html = stringWriter.ToString();
}
return html;
}
// CA2202 : Microsoft.Usage : Object 'stringWriter' can be disposed more than once in method 'Default.Func2()'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 45
public static string Func2()
{
string html;
StringWriter stringWriter = null;
try
{
using (var writer = new HtmlTextWriter(stringWriter = new StringWriter()))
{
// You would do some stuff with the writer here, but not for this example.
html = stringWriter.ToString();
}
}
finally
{
if (stringWriter != null)
stringWriter.Dispose();
}
return html;
}
// CA2202 : Microsoft.Usage : Object 'stringWriter' can be disposed more than once in
// method 'Default.Func3()'. To avoid generating a System.ObjectDisposedException
// you should not call Dispose more than one time on an object.: Lines: 61
public static string Func3()
{
string html;
using (var stringWriter = new StringWriter())
{
using (var writer = new HtmlTextWriter(stringWriter))
{
// You would do some stuff with the writer here, but not for this example.
html = stringWriter.ToString();
}
}
return html;
}
// CA2202 : Microsoft.Usage : Object 'stringWriter' can be disposed more than once in
// method 'Default.Func4()'. To avoid generating a System.ObjectDisposedException you
// should not call Dispose more than one time on an object.: Lines: 77
public static string Func4()
{
string html;
using (StringWriter stringWriter = new StringWriter())
{
using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
{
// You would do some stuff with the writer here, but not for this example.
html = stringWriter.ToString();
}
}
return html;
}
// CA2202 : Microsoft.Usage : Object 'stringWriter' can be disposed more than once in
// method 'Default.Func5()'. To avoid generating a System.ObjectDisposedException you
// should not call Dispose more than one time on an object.: Lines: 100
public static string Func5()
{
string html;
StringWriter stringWriter = null;
try
{
stringWriter = new StringWriter();
using (HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter))
{
// You would do some stuff with the writer here, but not for this example.
html = stringWriter.ToString();
}
}
finally
{
if (stringWriter != null)
stringWriter.Dispose();
}
return html;
}
修改您的Func5如下:
public static string Func5()
{
string html;
StringWriter stringWriter = null;
try
{
stringWriter = new StringWriter();
using (HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter))
{
stringWriter = null;
// You would do some stuff with the writer here, but not for this example.
html = htmlTextWriter.InnerWriter.ToString();
}
}
finally
{
if (stringWriter != null)
stringWriter.Dispose();
}
return html;
}
关键是将stringWriter变量设置为null(这不会影响HtmlTextWriter实例的InnerWriter),然后使用InnerWriter.ToString()获取HTML。
这实际上只是MSDN文章中示例的一个修改版本,该文章在前面的评论中引用,但专门适用于您的使用。
实际上没有办法使此代码避免警告,因为在这种特殊情况下,代码分析是错误的。
正确的代码是Func3,添加CodeAnalysis.SuppressMessage属性:
// Code Analysis is incorrectly assuming that HtmlTextWriter.Dispose will dispose of the InnerWriter, but it actually does not.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static string Func3()
{
string html;
using (var stringWriter = new StringWriter())
{
using (var writer = new HtmlTextWriter(stringWriter))
{
// You would do some stuff with the writer here, but not for this example.
// I prefer to use writer.InnerWriter as opposed to stringWriter for clarity.
html = writer.InnerWriter.ToString();
}
}
return html;
}
CA2202的文档使用了一个StreamWriter处理其Stream的示例,这是正确的,但HtmlTextWriter不处理其内部TextWriter(可通过子类化StringWriter并在override dispose中设置断点来验证)。这有点令人困惑,因为HtmlTextWriter派生自TextWriter,StringWriter也派生自TextWriter(与StreamWriter及其Stream是两个完全不同的类相反),那么HtmlTextWriter为什么需要InnerWriter?…但无论如何,这就是它的运作方式。
此外,文档中说不要取消显示此警告,因为"即使已知对象的Dispose可以安全地多次调用,将来实现也可能会更改。"但是,在这种情况下,Dispose不是被多次调用的,因此可以安全地取消显示警告。
但不要相信我的话!证据如下:
using System;
using System.IO;
using System.Web.UI;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
StreamWillBeDisposed();
TextWriterWillNotBeDisposed();
}
public static void StreamWillBeDisposed()
{
Stream stream = new DebugMemoryStream();
using (StreamWriter writer = new StreamWriter(stream))
{
// Use the writer object...
}// Underlying Stream will be disposed here by the StreamWriter
}
public static void TextWriterWillNotBeDisposed()
{
TextWriter stringWriter = new DebugStringWriter();
using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
{
// Use the writer object...
}// Underlying TextWriter will NOT be disposed here by the HtmlTextWriter
}
}
public class DebugMemoryStream : MemoryStream
{
protected override void Dispose(bool disposing)
{
// This Stream will be disposed when the StreamWriter is disposed
System.Diagnostics.Debugger.Break();
base.Dispose(disposing);
}
}
public class DebugStringWriter : StringWriter
{
protected override void Dispose(bool disposing)
{
// This code will never see the light of day
System.Diagnostics.Debugger.Break();
base.Dispose(disposing);
}
}
}
因为StringWriter是一次性的,所以您可以使用另一个包装您的内部编写器。
using (StringWriter stringWriter = new StringWriter())
{
using (var writer = new HtmlTextWriter(stringWriter))
{
html = stringWriter.ToString();
}
}
return html;