Область использования ключевого слова C#

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

Вот так:

using (SecureString s = new SecureString())
{

}

Вышеупомянутое легко понять: я могу использовать s как хочу в этих скобках, но как только я выйду из этих скобок, я больше не смогу ссылаться на s. Размах легко увидеть.

Но чего я не понимаю, так это того, как это работает, когда вы используете using без закрывающих скобок.

private void Function()
{
    // Some code here

    using (SecureString s = new SecureString())

    // more code here
}

Вы вообще не обязаны использовать скобки... так что... как я узнаю, где я могу использовать объект и куда он утилизируется, если для ключевого слова using нет скобок?


person Ryan Ries    schedule 17.10.2013    source источник
comment
Так же, как if или for без скобок.   -  person Vladimir    schedule 17.10.2013
comment
В стороне: на самом деле есть крайние случаи, когда это неправильно: stackoverflow.com/questions/573872/   -  person millimoose    schedule 17.10.2013
comment
Вы могли бы просто попробовать это. Это довольно легко выяснить с помощью нескольких простых экспериментов...   -  person Servy    schedule 17.10.2013
comment
@VladimirFrolov О, спасибо. Это имеет смысл. Я чувствую себя глупо, потому что не понимаю этого сейчас.   -  person Ryan Ries    schedule 17.10.2013


Ответы (12)


Почти в каждом случае в C#, когда у вас есть возможность использовать фигурные скобки, вы можете заменить их одной строкой кода.

Рассмотрим оператор if:

if (someBool)
    DoSomething();
else
    DoSomethingElse();

Это так же справедливо, как и следующее:

if (someBool)
{
    // do lots of things
}
else
    DoSomethingElse();

Это, вероятно, почти универсально верно для любого случая, когда вы можете использовать скобки { и }.

Хорошая вещь об этом с оператором using заключается в том, что вы можете вкладывать их следующим образом:

using (var stream = new NetworkStream(...))
using (var sslStream = new SslStream(stream))
using (var reader = new StreamReader(sslStream))
{
    reader.WriteLine(...);
}

Это эквивалентно следующему:

using (var stream = new NetworkStream(...))
{
    using (var sslStream = new SslStream(stream))
    {
        using (var reader = new StreamReader(sslStream))
        {
            reader.WriteLine(...);
        }
    }
}

Хотя, я думаю, вы согласитесь, что он выглядит гораздо симпатичнее.

person Trevor Elliott    schedule 17.10.2013
comment
Ничего себе, 10-12 правильных ответов на мой вопрос в течение 5 минут. Я не знаю, какой ответ выбрать. Я чувствую себя глупо, задавая такой очевидный вопрос сейчас. Но я выбираю этот, потому что вы указываете именно на те ситуации, которые вызывали у меня замешательство, с несколькими вложенными операторами using, которые не были должным образом разделены табуляцией/отступом, и это заставило меня чувствовать себя неуверенным в отношении области действия. Спасибо! - person Ryan Ries; 17.10.2013
comment
Если бы Visual Studio относилась к вложенным операторам using справедливо, она бы отступала для каждого из них так же, как и для других. Но это выглядело бы очень некрасиво. Он достаточно умен, чтобы вместо этого предпочесть стиль и аккуратность. - person Trevor Elliott; 17.10.2013
comment

using без фигурных скобок означает, что using находится в области видимости следующего оператора только — точно так же работает условие if.

using (SecureString s = new SecureString())
    s.Foo(); // Works
s.Foo(); // out of scope

Лично я всегда использую фигурные скобки, даже для конструкций с одним оператором if/using, чтобы избежать путаницы в таких ситуациях.

person Rory McCrossan    schedule 17.10.2013

Тогда это допустимо только в следующей команде после оператора using.

Обратите внимание, что это не означает следующая строка. Только следующая команда.

person Grzegorz W    schedule 17.10.2013

Объем по существу ограничен самой следующей строкой.

using(var s = new SecureString())
    s.Whatever();
s.Whatever() // ERROR! s is out of scope

Это то же самое, что использовать ключевое слово if без скобок:

if(x == y)
    x = 4;
person Dave Zych    schedule 17.10.2013

Для таких операторов, как using, if и foreach, если вы не включаете скобки, областью действия является просто следующий оператор (обычно следующая строка). Это обрабатывается так же, как вы включили скобки вокруг следующего утверждения. Например.

if (someCondition)
    Console.WriteLine("someCondition is true!");
Console.WriteLine("I run regardless");
// same as
if (someCondition)
{
    Console.WriteLine("someCondition is true!");
}

using (var s = new SecureString())
    Console.WriteLine(s); // this works!
//Console.WriteLine(s); // s is no longer in scope, this won't compile
person Tim S.    schedule 17.10.2013
comment
Дело не в том, что фигурные скобки добавляются автоматически; дело в том, что тело всех этих конструкций всегда представляет собой ровно один оператор; фигурные скобки позволяют рассматривать несколько операторов как один оператор. Если у вас уже есть одно утверждение, то они просто не нужны. Не похоже, что в синтаксисе фигурные скобки указаны как необязательные. Эти конструкции просто говорят, что за ними следует одно утверждение, каким бы оно ни было. - person Servy; 17.10.2013
comment
Верно, но я решил, что будет более разумно описать это так, как я это сделал, поскольку люди обычно думают об этом. - person Tim S.; 17.10.2013

Поскольку все ответили на это тем, что у вас может быть встроенный оператор использования, который эквивалентен этому:

using(var s = new SecureString()) s.Whatever();

Однако, пожалуйста, не делайте этого, так как, на мой взгляд, вы сделаете код менее читаемым и запутаете других разработчиков. Я думаю, что этот момент хорошо сделан тем фактом, что вы задали этот вопрос.

person Ben Robinson    schedule 17.10.2013

Чтобы расширить другие ответы:

using (SecureString s = new SecureString())
    // This statement is in using scope.

// This is not.
person Jason Evans    schedule 17.10.2013
comment
Если вы собираетесь проголосовать против, по крайней мере, имейте разум, чтобы прокомментировать, почему мой ответ достоин проголосовать против. Иначе как я могу учиться на своем неправильном ответе? - person Jason Evans; 17.10.2013
comment
Дополнительные мысли о том, как уточнить назначение кода: stackoverflow.com/a/22675224/618649 - person Craig; 27.03.2014

Если скобок нет, то получится одна строка. То же самое с операторами «если» или «для». Например:

for (int i = 0; i < 10; i++)
    Console.WriteLine(i.ToString());

if (str == "hello")
    Console.WriteLine("hello back!");

using (SecureString s = new SecureString())
    Console.WriteLine("Here we have s");

Console.WriteLine("but here it's out of scope already");
person andrewpm    schedule 17.10.2013

Если вы не используете скобки, допустимо только следующее утверждение

using (SecureString s = new SecureString())
    // statement here

Если вы хотите включить более одного утверждения, используйте скобки.

person gleng    schedule 17.10.2013
comment
Точнее, это следующий оператор, который может занимать несколько строк или даже находиться на предыдущей строке. И если строка состоит из нескольких операторов, только первый оператор будет внутри оператора using. - person Jon Skeet; 17.10.2013
comment
@JonSkeet Ты абсолютно прав. Я обновил свой ответ для ясности. - person gleng; 17.10.2013

using, точно так же, как for, while, if, else и многие другие ключевые слова, всегда принимают только одно выражение в качестве «тела» блока.

Если программист хочет выполнить несколько операторов в теле, он может использовать фигурные скобки для создания сложного оператора, то есть одного оператора, который выполняет несколько (или ноль) операторов. Когда вы используете фигурные скобки после using или if, вы говорите: «Все здесь должно рассматриваться как одно выражение». Если у вас уже было ровно одно выражение для начала, нет необходимости использовать фигурные скобки, чтобы превратить его в одно выражение, так что вы можете их опустить.

person Servy    schedule 17.10.2013

Глядя на разборку, вы увидите, что именно происходит.

  static private void Function()
  {
     Console.WriteLine("before");

     using (SecureString s = new SecureString())
        s.Clear();

     Console.WriteLine("after");
  }

Используя IL Dissassembler, приведенная выше функция дизассемблируется в приведенный ниже код. Вы можете видеть, что оператор using реализован с try-finally, обертывающим остальную часть оператора (который может быть блоком, определяемым фигурными скобками, но в данном случае это просто точка с запятой). Используемый вами объект расположен в блоке finally. Затем за пределами finally код продолжается со следующего оператора из источника.

.method private hidebysig static void  Function() cil managed
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Security.SecureString s,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "before"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  newobj     instance void [mscorlib]System.Security.SecureString::.ctor()
  IL_0011:  stloc.0
  .try
  {
    IL_0012:  ldloc.0
    IL_0013:  callvirt   instance void [mscorlib]System.Security.SecureString::Clear()
    IL_0018:  nop
    IL_0019:  leave.s    IL_002b
  }  // end .try
  finally
  {
    IL_001b:  ldloc.0
    IL_001c:  ldnull
    IL_001d:  ceq
    IL_001f:  stloc.1
    IL_0020:  ldloc.1
    IL_0021:  brtrue.s   IL_002a
    IL_0023:  ldloc.0
    IL_0024:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0029:  nop
    IL_002a:  endfinally
  }  // end handler
  IL_002b:  nop
  IL_002c:  ldstr      "after"
  IL_0031:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0036:  nop
  IL_0037:  ret
} // end of method Program::Function
person jltrem    schedule 17.10.2013

Как упоминалось в других комментариях, при использовании оператора using, как вы упомянули, это происходит только тогда, когда у вас есть одна строка.

Я хотел бы добавить, что в C# 8.0 вы можете использовать используя объявления следующим образом:

private void Function()
{
    // Some code here

    using SecureString s = new SecureString();

    // more code here
}

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

person Misha Zaslavsky    schedule 16.07.2021