Как использовать StringWriter и HtmlWriter вместе без предупреждений анализа кода - PullRequest
5 голосов
/ 11 января 2012

Я использую .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;
}

Ответы [ 3 ]

1 голос
/ 17 сентября 2013

Измените ваш 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 (которое не влияет на InnerWriter экземпляра HtmlTextWriter), а затем использовать InnerWriter.ToString () для получения HTML-кода.

На самом деле это всего лишь модифицированная версия примера в статье MSDN, на которую есть ссылка в предыдущем комментарии, но которая относится конкретно к вашему использованию.

1 голос
/ 06 декабря 2014

На самом деле нет способа заставить этот код избегать предупреждений, потому что в данном конкретном случае анализ кода неправильный.

Правильный код - 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 его потока, который является правильным, но HtmlTextWriter не избавляется от своего внутреннего TextWriter (проверяется путем создания подкласса StringWriter и установки точки останова в переопределенной утилизации) , Это немного сбивает с толку, поскольку HtmlTextWriter является производным от TextWriter, а StringWriter также является производным от TextWriter (в отличие от StreamWriter и его Stream, являющихся двумя совершенно разными классами), так почему HtmlTextWriter нужен InnerWriter?… Но в любом случае, именно так он работает .

Кроме того, в документации говорится, что не следует подавлять это предупреждение, поскольку «Даже если известно, что Dispose для объекта можно безопасно вызывать несколько раз, реализация может измениться в будущем». Однако в этом случае Dispose будет not вызывается несколько раз, поэтому предупреждение можно безопасно подавить.

Но не верьте мне на слово! Вот доказательство:

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);
        }
    }

}
0 голосов
/ 11 января 2012

Поскольку StringWriter одноразовый, вы можете обернуть свой внутренний писатель другим, используя.

using (StringWriter stringWriter = new StringWriter())
{
    using (var writer = new HtmlTextWriter(stringWriter))
    {
         html = stringWriter.ToString();  
    }
}
return html;
...