повернення в середині використовуючого блоку


196

Щось на зразок:

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

Я вважаю, що це не належне місце для заяви про повернення, чи не так?

Відповіді:


194

Як зазначають кілька інших, загалом це не проблема.

Єдиний випадок, який викличе у вас проблеми, - це якщо ви повернетеся в середину використовуючого оператора та додатково повернете змінну за допомогою. Але знову ж таки, це також спричинить проблеми, навіть якщо ви не повернетесь і просто зберегли посилання на змінну.

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

147

Це абсолютно чудово.

Ви, мабуть, так думаєте

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

сліпо перекладається на:

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

Що, правда, буде проблемою, і це зробить usingзаяву досить безглуздою --- саме тому це не те, що вона робить.

Компілятор гарантує, що об’єкт розміщений до того, як контроль покине блок - незалежно від того, як він залишає блок.


7
Я був, мабуть.
тафа

Чудова відповідь @James Curran! Але мені досить цікаво, на що це перекладено. Або це виражається лише в ІЛ? (яку я ніколи раніше не намагався читати).
Барт

1
@Bart - я вважаю це оцінкою виразного повернення у тимчасову змінну, потім виконуючи dispose, а потім повернення тимчасової змінної.
ToolmakerSteve

@James Curran Зверху до цього, Ви тільки пояснили, що сталося на задньому плані. Велике дякую.
Sercan Timoçin

@Bart це, мабуть, перекладено на: спробуйте {... ваш код ...} нарешті {x.Dispose (); }
Bip901

94

Це абсолютно добре - зовсім не проблема. Чому ви вважаєте, що це неправильно?

Оператор, що використовує, - це лише синтаксичний цукор для блоку спробу / нарешті, і, як каже Grzenio, також непогано повертатися і з блоку спробу.

Вираз повернення буде оцінено, потім остаточно буде виконаний блок, потім повернеться метод.


5
Відповідь Джеймса Куррана пояснює, що я думав.
тафа

27

Це буде прекрасно працювати, як і повернення в середині try{}finally{}


18

Це цілком прийнятно. Використовуючи оператор забезпечує IDisposable об'єкт буде розташований ні на що.

Від MSDN :

Оператор, що використовує, забезпечує виклик Dispose, навіть якщо виняток виникає під час виклику методів на об'єкті. Ви можете досягти такого ж результату, помістивши об'єкт всередину спробу блоку, а потім викликаючи Dispose в остаточному блоці; насправді, саме так компілятор перекладає використовуючий оператор.


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 # розпоряджаються на замовлення, наприклад, клієнти WCF - це таке, що використовується, наприклад, повернення "не може отримати доступ до об'єкта"
OzBob,

-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.