Можно ли объединить блок 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
  }
}