Можно ли объединить блок using() {} с параметром out out метода?
Для метода
public static bool Connection.TryCreate(out Connection connection) {}
И кусок кода вызова:
Connection connection;
if (!Connection.TryCreate(out connection))
// handle failure gracefully.
/*
* work with connection
*
* …
*
*/
connection.Dispose();
Я использую тот же шаблон, что и bool.TryParse
, а друзья, т.е. TryCreate
возвращает, была ли операция успешной.
Я понимаю, что переменная using()
должна быть доступна только для чтения в своем блоке, но есть способ превратить это в блок using() {}
(TryCreate
устанавливает его только один раз), например:
using (Connection connection)
{
if (!Connection.TryCreate(out connection))
// this would leave the using() block prematurely
/*
* work with sconnection
*
* …
*
*/
}
(Это не скомпилировано:
ошибка CS1657: не может передать "соединение" в качестве аргумента ref или out, потому что это "использование переменной"
)
Ответы
Ответ 1
Похоже на плохое использование шаблона Try*
(некоторые утверждают, что это анти-шаблон).
Вместо TryCreate
просто используйте метод Create
, который генерирует исключение, если оно не выполнено успешно, и возвращает созданное соединение.
Тогда вы можете сделать обычное:
using(Connection connection = Connection.Create())
{
}
В качестве альтернативы, если вы хотите избежать генерирования исключения и требуемого try{}catch{}
, возвращайте метод Create
null
, когда соединение не может быть создано и проверено для этого.
Ответ 2
Нет, это невозможно.
Конструкция using (x) {...}
создает копию x
при входе в блок, поэтому вы можете сделать это:
var x = new FileStream(...);
using (x)
{
x = null;
}
Поток все равно будет удален, когда закончится блок using
.
Как следствие, это тоже не сработает:
Stream x = null;
using (x)
{
x = new FileStream(...);
}
здесь поток, который вы создаете внутри блока using, не будет удален.
Однако вы можете сделать следующее:
Connection connection;
if (Connection.TryCreate(out connection))
using (connection)
{
}
В С# 7.0 и далее вы можете комбинировать это с "переменными" в форме:
if (Connection.TryCreate(out var connection))
using (connection)
{
}
Ответ 3
Вы можете сделать это следующим образом:
Connection connection;
if (Connection.TryCreate(out connection))
{
using (connection)
{
…
}
}
Но было бы лучше, если бы вы только что вернули null
при ошибке:
using (Connection connection = Connection.Create())
{
if (connection != null)
{
…
}
}
Блок finally
, созданный using
, проверяет, является ли connection
null
и ничего не делает, если он есть.
Кроме того, если вы не объявляете переменную в using
, тогда она не должна быть только для чтения.
Ответ 4
Нет. Если вас беспокоят исключения из разрыва между вызовом метода и использованием, вы можете использовать try/finally:
Connection conn = null;
try {
if(!conn.TryCreate(out conn)) return;
...
} finally {
if(conn != null) conn.Dispose();
}
Ответ 5
Шаг в сторону?
public class ConnectTo : IDisposable
{
public Connection CurrentConnection {get; private set;}
public ConnectTo()
{
CurrentConnection = null;
// Connect up to whatever.
}
#region IDisposable
// Blah blah
#endregion
}
затем
using( ConnectedTo conn = new ConnectTo())
{
if (conn.CurrentConnection != null)
{
//Do Stuff
}
}