Лучшая практика для вложенных операторов?

У меня есть блок кода следующим образом, и я использую 3 вложенных блока using.

Я обнаружил, что с помощью блоков try finally я могу избежать этого, но если есть более двух операторов using, то какой лучший подход?

private FileStream fileStream = null;
private Document document = null;
private PdfWriter pdfWriter =  null;

using (fileStream = new FileStream("ABC.pdf", FileMode.Create))
{
    using (document = new Document(PageSize.A4, marginLeft, marginRight, marginTop, marginBottom))
    {
        using (pdfWriter = PdfWriter.GetInstance(document, fileStream))
        {
            document.AddAuthor(metaInformation["author"]);
            document.AddCreator(metaInformation["creator"]);
            document.AddKeywords("Report Generation using I Text");
            document.AddSubject("Document subject");
            document.AddTitle("The document title");
        }
    }
}

Ответ 1

Вы можете удалить отступ вложенности следующим образом:

using (var fileStream = new FileStream("ABC.pdf", FileMode.Create))
using (var document = new Document(PageSize.A4, marginLeft, marginRight, marginTop, marginBottom))
using (var pdfWriter = PdfWriter.GetInstance(document, fileStream))
{
   // code
}

Ответ 2

Немного менее подробный способ избежать отступов:

  using (var fileStream = new FileStream("ABC.pdf", FileMode.Create))
  using (var document = new Document(PageSize.A4, marginLeft, marginRight, marginTop, marginBottom))
  using (var pdfWriter = PdfWriter.GetInstance(document, fileStream))
  {
       document.AddAuthor(metaInformation["author"]);
       document.AddCreator(metaInformation["creator"]);
       document.AddKeywords("Report Generation using I Text");
       document.AddSubject("Document subject - Describing the steps creating a PDF document");
       document.AddTitle("The document title - PDF creation using iTextSharp");
   }

Как указал Джон Скит, нет необходимости в том, чтобы эти переменные являлись переменными экземпляра, поскольку они расположены после блоков using в любом случае.

Вместо этого вы можете использовать локальные переменные, как показано в приведенном выше коде.

Ответ 3

Может быть, что-то условное; лучший подход для выбора между двумя, на мой взгляд, будет:

  • Using: Если вы собираетесь использовать экземпляр в контексте и должны Dispose его после того, как вы закончите с ним
  • try/finally: Если вы ожидаете какой-либо проблемы и имеете какое-то отношение к исключению, перехватите ее перед тем, как вы Dispose используете экземпляр, который вы используете.

И как утверждают другие комментарии/ответы; вам не нужны переменные уровня экземпляра;

using (FileStream fileStream = new FileStream("ABC.pdf", FileMode.Create))
using (Document document = new Document(PageSize.A4, marginLeft, marginRight, marginTop, marginBottom))
using (PdfWriter pdfWriter = PdfWriter.GetInstance(document, fileStream))
{
    // # Implementation here seems like a good approach
}

Ответ 4

В одном методе, когда вам не нужно обрабатывать или изменять данные; выбор, предложенный Яном Соммером, был бы моим выбором. Однако в некоторых случаях одноразовый список полезен. В частности, если у вас много одноразовых полей, которые нужно удалить (в этом случае вы не можете использовать их).

Требуется, чтобы вы не добавили элемент в список. (Хотя вы могли бы также сказать, что вы должны не забывать использовать его.) Прерывает процесс удаления, если один из методов размещения выдает, оставляя оставшиеся предметы неактивными.

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        if (this.Count > 0)
        {
            List<Exception> exceptions = new List<Exception>();

            foreach (var disposable in this)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception e)
                {
                    exceptions.Add(e);
                }
            }
            base.Clear();

            if (exceptions.Count > 0)
                throw new AggregateException(exceptions);
        }
    }

    public T Add<T>(Func<T> factory) where T : IDisposable
    {
        var item = factory();
        base.Add(item);
        return item;
    }
}

Теперь поймайте любые исключения из вызовов Dispose и выкиньте новое AggregateException после прохождения всех элементов. Я добавил вспомогательный метод Add, который позволяет более простое использование:

using (var disposables = new DisposableList())
{
        var file = disposables.Add(() => File.Create("test"));
        // ...
        var memory = disposables.Add(() => new MemoryStream());
        // ...
        var cts = disposables.Add(() => new CancellationTokenSource());
        // ... 
}