Нужно ли гнездовать с помощью IDisposable объектов?

Нужно ли обертывать все мои объекты IDisposable в операторы using(){}, даже если я просто перехожу один к другому? Например, следующим способом:

public static string ReadResponse(HttpWebResponse response)
{
    string resp = null;
    using (Stream responseStream = response.GetResponseStream())
    {
        using (StreamReader responseReader = new StreamReader(responseStream))
        {
            resp = responseReader.ReadToEnd();
        }
    }
    return resp;
}

Могу ли я объединить это только с одним using следующим образом:

public static string ReadResponse(HttpWebResponse response)
{
    string resp = null;
    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        resp = reader.ReadToEnd();
    }
    return resp;
}

Могу ли я рассчитывать на размещение как Stream, так и StreamReader? Или мне нужно использовать два оператора using?

Ответ 1

Да, вы можете, но это потому, что документация конструктора StreamReader специально говорит: "Объект StreamReader вызывает Dispose на предоставленном объекте Stream при вызове StreamReader.Dispose."

Если это не так, вы можете сделать что-то вроде этого, чтобы хотя бы немного очистить код.

using (Stream responseStream = response.GetResponseStream())
using (StreamReader responseReader = new StreamReader(responseStream))
{
    resp = responseReader.ReadToEnd();
}

Ответ 2

Человек/код/​​слой, создавший одноразовый объект, должен в целом нести ответственность за удаление объекта. Однако есть сценарии, которые могут возникнуть там, где это не так, и все в порядке. Это становится проблемой тогда документации.

Ответ 3

Я нашел этот вопрос более широким, чем простой вопрос о размещении инструкции using, это довольно интересная проблема проектирования приложений.

Нужно ли обертывать все мои объекты IDisposable при использовании() {} операторов, даже если я просто перехожу один к другому?

Да, поскольку вы создаете экземпляр объекта, который реализует IDisposable - вам известно об утилизации, либо путем переноса в using(), либо явного вызова Dispose().

Причина этого проста, представьте себе следующий сценарий: у вас есть следующие сущности

  • TransportService
  • ReportService
  • FeedService

все реализует IDisposable. Оба ReportService и FeedService требуют, чтобы экземпляр TransportService передавался в стадии строительства. И вопрос - правильно ли это распоряжаться TransportService в Dispose() ReportService или FeedService? Нет! Безусловно, один и тот же экземпляр транспортного сервиса может быть передан в обеих службах, и как только один из них будет транспортироваться - это повлияет и на все сервисы.

public sealed class ReportService : IDisposable
{       
   private readonly ITransportService transportService;

   public ReportService(ITransportService transportService)
   {
       this.transportService = transportService;
   }

   public Dispose()
   {
      // ReportService should not dispose objects
      // passed in since they could be used by other classes as well
      // DO NOT: transportService.Dispose();
   }
}