возвращаясь в середине использования блока


197

Что-то вроде:

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

Я считаю, что это не подходящее место для ответного заявления, не так ли?

Ответы:


195

Как отметили несколько других в целом, это не проблема.

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

using ( var x = new Something() ) { 
  // not a good idea
  return x;
}

Так же плохо

Something y;
using ( var x = new Something() ) {
  y = x;
}

1
Просто я собирался отредактировать свой вопрос по поводу упомянутой вами темы. Спасибо.
— Тафа

Пожалуйста, помогите мне понять, почему это плохо. Я хотел бы вернуть поток, который я использую в качестве вспомогательной функции, в другую функцию для обработки изображений. Похоже, поток будет ликвидирован, если я сделаю это?
— Джон Шедлецкий

3
@JohnShedletsky В этом случае вызов вашей функции должен быть заключен в использование. Как с использованием (Stream x = FuncToReturnStream ()) {...} и без использования внутри FuncToReturnStream.
— Феликс Кейл

@JohnShedletsky Я уверен, что это потому, что returnоператор делает конец usingблока недоступным для любых путей кода. Конец usingблока должен быть пройден, чтобы при необходимости объект можно было утилизировать.
— facepalm42

148

Это прекрасно.

Вы, очевидно, думаете, что

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

слепо переводится на:

IDisposable disposable = GetSomeDisposable()
//.....
//......
return Stg();
disposable.Dispose();

Что, по общему признанию, было бы проблемой, и сделало бы usingзаявление довольно бессмысленным - вот почему это не то, что он делает.

Компилятор обеспечивает удаление объекта до того, как элемент управления покидает блок - независимо от того, как он покидает блок.


7
Я был, по-видимому.
— Тафа

Отличный ответ @ Джеймс Керран! Но это делает меня довольно любопытным, на что оно переведено. Или это выражается только в IL? (который я никогда не пробовал читать раньше).
— Барт

1
@Bart - я думаю об этом как об оценке возвращаемого выражения во временной переменной, затем выполнении dispose, а затем возвращении временной переменной.
— ToolmakerSteve

@ Джеймс Керран. Сверху сюда, Только ты объяснил, что произошло на заднем плане. Большое спасибо.
— Серкан Тимошин

@Bart, вероятно, переведено на: try {... your code ...} finally {x.Dispose (); }
— Bip901

94

Это абсолютно нормально - никаких проблем. Почему вы считаете, что это неправильно?

Оператор using - это всего лишь синтаксический сахар для блока try / finally, и, как говорит Грженио, можно также вернуться из блока try.

Возвращаемое выражение будет оценено, затем будет выполнен блок finally, затем метод вернется.


5
Ответ Джеймса Керрана объясняет, о чем я думал.
— Тафа

27

Это будет прекрасно работать, так же как возвращение в середине try{}finally{}


18

Это полностью приемлемо. Используя оператор обеспечивает IDisposable объект будет расположен ни на что.

Из MSDN :

Оператор using гарантирует, что Dispose вызывается, даже если возникает исключение во время вызова методов для объекта. Вы можете достичь того же результата, поместив объект в блок try, а затем вызвав Dispose в блоке finally; на самом деле, именно так оператор using переводится компилятором.


14

Код ниже показывает, как usingработает:

private class TestClass : IDisposable
{
   private readonly string id;

   public TestClass(string id)
   {
      Console.WriteLine("'{0}' is created.", id);
      this.id = id;
   }

   public void Dispose()
   {
      Console.WriteLine("'{0}' is disposed.", id);
   }

   public override string ToString()
   {
      return id;
   }
}

private static TestClass TestUsingClose()
{
   using (var t1 = new TestClass("t1"))
   {
      using (var t2 = new TestClass("t2"))
      {
         using (var t3 = new TestClass("t3"))
         {
            return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3));
         }
      }
   }
}

[TestMethod]
public void Test()
{
   Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString());
}

Вывод:

't1' создан.
't2' создан.
't3' создан.
«Создано из t1, t2, t3» создано.
't3' расположен.
't2' расположен.
't1' расположен.

Распоряжение вызывается после оператора return, но перед выходом из функции.


1
Обратите внимание, что некоторые объекты C # располагаются по
— своему

-4

Возможно, это не на 100% правда, что это приемлемо ...

Если вам случится вложить и вернуться из вложенного, это может быть небезопасно.

Возьмите это как пример:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
            return memoryStream.ToArray();
        }
    }
}

Я передавал DataTable для вывода в виде CSV. С возвратом в середине он записывал все строки в поток, но в выводимом csv всегда отсутствовала строка (или несколько, в зависимости от размера буфера). Это сказало мне, что что-то не было закрыто должным образом.

Правильный способ - убедиться, что все предыдущие использования расположены правильно:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
        }
    }

    return memoryStream.ToArray();
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.