возврат в середине блока использования
-
20-08-2019 - |
Вопрос
Что-то вроде:
using (IDisposable disposable = GetSomeDisposable())
{
//.....
//......
return Stg();
}
Я считаю, что это не подходящее место для оператора возврата, не так ли?
Решение
Как отмечали некоторые другие, в целом это не проблема.
Единственный случай, когда это вызовет у вас проблемы, — это если вы вернетесь в середине оператора using и дополнительно вернете переменную in using.Но опять же, это также вызовет у вас проблемы, даже если вы не вернетесь, а просто сохраните ссылку на переменную.
using ( var x = new Something() ) {
// not a good idea
return x;
}
Так же плохо
Something y;
using ( var x = new Something() ) {
y = x;
}
Другие советы
Это совершенно нормально.
Вы, видимо, думаете, что
using (IDisposable disposable = GetSomeDisposable())
{
//.....
//......
return Stg();
}
слепо переводится на:
IDisposable disposable = GetSomeDisposable()
//.....
//......
return Stg();
disposable.Dispose();
Что, по общему признанию, было бы проблемой и сделало бы using
заявление довольно бессмысленное --- вот почему нет что оно делает.
Компилятор гарантирует, что объект будет удален до того, как элемент управления покинет блок — независимо от того, как он покинет блок.
Это абсолютно нормально – никаких проблем.Почему вы считаете, что это неправильно?
Оператор using — это всего лишь синтаксический сахар для блока try/finally, и, как говорит Грзенио, возврат из блока try тоже вполне допустим.
Будет вычислено возвращаемое выражение, затем будет выполнен блокfinally, а затем метод вернется.
Это будет работать отлично, так же, как возвращение в середине try{}finally{}
Это вполне приемлемо.А с использованием Оператор гарантирует, что объект IDisposable будет удален, несмотря ни на что.
От MSDN:
Оператор using гарантирует, что Dispose будет вызван, даже если при вызове методов объекта возникнет исключение.Вы можете добиться того же результата, поместив объект в блок try, а затем вызвав Dispose в блокеfinally;на самом деле именно так компилятор транслирует оператор using.
Код ниже показывает, как 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, но до выхода из функции.
Возможно, это не на 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();
}