Подтвердить что ты не робот

Можно ли объявить объект над оператором using, а не в скобках

Большинство примеров оператора using в С# объявляют объект внутри скобок следующим образом:

using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection))
{
   // Code goes here
}

Что произойдет, если я использую оператор using следующим образом с объявленным объектом вне оператора using:

SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection);
using (cmd)
{
   // Code goes here
}

Неплохая идея использовать оператор using в моем примере во втором примере и почему?

4b9b3361

Ответ 1

Объявление переменной внутри выражения управления выражением оператора ограничивает область видимости переменной внутри оператора using. Во втором примере переменная cmd может продолжать использоваться после инструкции using (когда она будет удалена).

Как правило, рекомендуется использовать переменную только для одной цели, ограничивая ее область действия, позволяя другую команду с тем же именем позже в области видимости (возможно, в другом, используя выражение). Возможно, что более важно, что он говорит читателю вашего кода (и обслуживание требует больше усилий, чем первоначальное письмо), что cmd не используется вне выражения using: ваш код немного понятнее.

Ответ 2

Да, это действительно - объект все равно будет располагаться таким же образом, то есть в конце, и если поток выполнения пытается покинуть блок (возврат/исключение).

Однако, если вы попытаетесь использовать его снова после using, он будет удален, поэтому вы не можете знать, безопасен ли этот экземпляр для продолжения использования, поскольку dispose не должен reset состояния объекта. Кроме того, если во время построения возникает исключение, оно не попадет в блок using.

Я бы объявил и инициализировал переменную внутри оператора, чтобы определить ее область. Скорее всего, вы не будете нуждаться в ней вне области видимости, если вы все равно используете using.

MemoryStream ms = new MemoryStream(); // Initialisation not compiled into the using.

using (ms) { } 

int i = ms.ReadByte(); // Will fail on closed stream.

Ниже приведено, но в большинстве случаев несколько ненужное:

MemoryStream ms = null;

using (ms = new MemoryStream())
{ }

// Do not continue to use ms unless re-initializing.

Ответ 4

Ответ был дан, и ответ: Да, это возможно.
Однако, с точки зрения программистов, не делайте этого!. Это смутит любого программиста, который будет работать по этому коду и кто не ожидает такой конструкции. В принципе, если вы отдаете код кому-то другому, чтобы работать, этот другой человек может оказаться очень смущенным, если после использования будет использовать переменную cmd. Это становится еще хуже, если еще больше строк кода между созданием объекта и" использованием".

Ответ 5

Я написал небольшой код вместе с некоторыми модульными тестами. Мне нравится, когда я могу проверять утверждения по этому вопросу. Мои выводы:

  • Независимо от того, создан ли объект до или в инструкции using, не имеет значения. Он должен реализовать IDisposable, а Dispose() вызывается при выходе из блока операторов using (закрывающая скобка).
  • Если конструктор генерирует исключение при вызове в операторе using Dispose(), он не будет вызываться. Это разумно, поскольку объект не был успешно сконструирован, когда исключение выбрано в конструкторе. Поэтому в этой точке не существует экземпляра, и вызывающие члены экземпляра (нестатические элементы) на объекте не имеют смысла. Это включает Dispose().

Чтобы воспроизвести мои выводы, обратитесь к исходному коду ниже.

Итак, в нижней строке вы можете - как указано другими - создать экземпляр объекта перед оператором using, а затем использовать его внутри оператора using. Я также согласен, однако, что перемещение конструкции за пределы используемой инструкции приводит к тому, что код становится менее читаемым.

Еще один элемент, о котором вы можете знать, - это тот факт, что некоторые классы могут генерировать исключение в реализации Dispose(). Хотя руководство не должно этого делать, даже Microsoft имеет случаи этого, например, как обсуждалось здесь.

Итак, вот мой исходный код включает (длительный) тест:

  public class Bar : IDisposable {
     public Bar() {
        DisposeCalled = false;

     }
     public void Blah() {
        if (DisposeCalled) {
           // object was disposed you shouldn't use it anymore
           throw new ObjectDisposedException("Object was already disposed.");
        }
     }

     public void Dispose() {
        // give back / free up resources that were used by the Bar object
        DisposeCalled = true;
     }

     public bool DisposeCalled { get; private set; }
  }

  public class ConstructorThrows : IDisposable {
     public ConstructorThrows(int argument) {
        throw new ArgumentException("argument");
     }

     public void Dispose() {
        Log.Info("Constructor.Dispose() called.");
     }
  }

  [Test]
  public void Foo() {
     var bar = new Bar();
     using (bar) {
        bar.Blah(); // ok to call
     }// Upon hitting this closing brace Dispose() will be invoked on bar.

     try {
        bar.Blah(); // Throws ObjectDisposedException
        Assert.Fail();
     }
     catch(ObjectDisposedException) {
        // This exception is expected here
     }

     using (bar = new Bar()) { // can reuse the variable, though
        bar.Blah(); // Fine to call as this is a second instance.
     }

     // The following code demonstrates that Dispose() won't be called if 
     // the constructor throws an exception:
     using (var throws = new ConstructorThrows(35)) {

     }
  }