Когда вы используете scope без оператора в C#?

совсем недавно я узнал, что вы можете сделать это в C#:

{
    // google
    string url = "#";

    if ( value > 5 )
        url = "http://google.com";

    menu.Add( new MenuItem(url) );
}
{
    // cheese
    string url = "#"; // url has to be redefined again, 
                      // so it can't accidently leak into the new menu item

    if ( value > 45 )
        url = "http://cheese.com";

    menu.Add( new MenuItem(url) );
}

вместо т. е.:

    string url = "#";

    // google
    if ( value > 5 )
        url = "http://google.com";

    menu.Add( new MenuItem(url) );


    // cheese
    url = "#"; // now I need to remember to reset the url

    if ( value > 45 )
        url = "http://cheese.com";

    menu.Add( new MenuItem(url) );

Это может быть плохой пример, который можно решить многими другими способами.

есть ли какие-либо шаблоны, где функция "область без оператора" is хорошая практика?

5 ответов


Если вы спросите, почему они реализовали эту функцию?

Я разработал свой собственный язык, и я обнаружил, что синтаксис "scope without a statement" очень легко проникает в грамматику. это побочный эффект, если хотите, при написании грамматики, которая просто работает красиво и легко читается.

на моем языке у меня такая же функциональность, и я никогда не "проектировал" для нее явно - Я получил его бесплатно!--5-->. Я никогда не садился за стол, и подумал "О, было бы здорово иметь такую "особенность"?". Черт возьми, сначала я даже не знал, что моя грамматика допускает это.

'{} 'является" составным оператором", поскольку это упрощает синтаксис для все места, которые вы хотите использовать (conditionals, тела цикла, п.).. и потому что это позволяет вам опустить скобки, когда один оператор контролируется ('if (a

тот факт, что его можно использовать в любом месте оператора может появиться водопад прямо из этого; это безвредно, и иногда полезно, как другие ответы сказали. - Если она не сломана, не чини ее. - кешлам.

Итак, вопрос не столько в том, "почему они его реализовали", а скорее: "почему они его не запретили / почему они это разрешили?"

могу ли я запретить эту конкретную функциональность на моем языке? Конечно, но я не вижу причин - это будет дополнительная плата для компании.

Итак, эта история может быть, или не может быть правдой для C#, но я не разрабатывал язык (и я действительно не языковой дизайнер), поэтому трудно сказать, почему именно, но подумал, что я все равно упомяну об этом.

та же функциональность находится в C++, который фактически имеет некоторые варианты использования - он позволяет финализатору объекта запускаться детерминированно, если объект выходит из области, хотя это не относится к C#.


тем не менее, я не использовал этот конкретный синтаксис в течение моих 4 лет C# ( embedded-оператор - > блок, когда речь идет о конкретных терминах), ни я не видел, чтобы он использовался где-либо. Ваш пример умоляет о том, чтобы вы перешли к методам.

взгляните на грамматику C#:http://msdn.microsoft.com/en-us/library/aa664812%28v=vs.71%29.aspx

кроме того, как сказал Йеппе, я использовал синтаксис " {}", чтобы убедиться, что каждый блок case в конструкции "switch" имеет отдельную локальную область:

int a = 10;
switch(a)
{
    case 1:
    {
        int b = 10;
        break;
    }

    case 2:
    {
        int b = 10;
        break;
    }
}

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


поздним добавлением:

блоки локальной области { ... } присутствующие в источнике c# не кажутся релевантными для результирующего байт-кода IL. Я попробовал этот простой пример:

static void A()
{
    {
        var o = new object();
        Console.WriteLine(o);
    }

    var p = new object();
    Console.WriteLine(p);
}

static void B()
{
    var o = new object();
    Console.WriteLine(o);

    var p = new object();
    Console.WriteLine(p);
}


static void C()
{
    {
        var o = new object();
        Console.WriteLine(o);
    }

    {
        var o = new object();
        Console.WriteLine(o);
    }
}

это было скомпилировано в релиз режим (оптимизация включена). Полученный IL в соответствии с IL Это-это:

.method private hidebysig static void  A() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object p)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::A

.method private hidebysig static void  B() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object p)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::B

.method private hidebysig static void  C() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object V_1)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::C

выводы:

  • объявления локальной области не существуют ни в какой форме в байт-коде IL.
  • компилятор C# выберет новые имена для локальных переменных, которые в противном случае были бы в конфликте (вторая из переменных o на C метод).
  • люди, которые чувствуют (см. другие ответы и комментарии к вопросу), что сборщик мусора может уметь делать свою работу раньше, когда они вводят локальную область в источник c#, неправильно.

я также попытался скомпилировать это в отладка режим (без оптимизаций). Начало и конец локальной области, похоже, отображаются как nop - инструкции только ("операции"). В некоторых случаях две одинаково именованные локальные переменные из разных локальных областей были сопоставлены с то же самое локальная переменная в IL, например, с именем метода C# C выше. Такое "объединение" двух переменных возможно только в том случае, если их типы совместимы.


вы могли бы использовать {} для перепрофилирования имен переменных (даже для разных типов):

{
   var foo = new SomeClass();
}
{
   Bar foo = new Bar(); // impairs readability
}

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

таким образом, вместо использования "незапрошенного" блока области без предыдущего оператора, в большинстве случаев код должен быть соответственно преобразован в отдельные функции.

редактировать

IMO, в любое время есть необходимость императивно сбросить локальный изменяемые значения переменных или перепрофилирование их для дополнительных или альтернативных проблем является признаком запаха. например, исходный код может быть переработан как:

menu.Add( value > 5 
            ? new MenuItem("http://google.com")
            : new MenuItem("#"));

menu.Add( value > 45 
            ? new MenuItem("http://cheese.com")
            : new MenuItem("#"));

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

(или new MenuItem(value > 45 ? "http://cheese.com" : "#"), или создать перегрузку MenuItem по умолчанию #, или перейти в создание MenuItem на заводе метод и т. д.)

редактировать
Re: объем не имеет никакое влияние на продолжительности жизни

область может использоваться в рамках метода для ограничения срока службы дорогостоящих объектов

в моем первоначальном посте неправильно указано, что локальная область может использоваться для влияния на срок службы объектов. Это неверно, так как DEBUG и RELEASE строит, и независимо от того, перераспределено ли имя переменной или нет, как демонстрируется разборкой Иль Йеппе, и этими модульные тесты здесь. Спасибо Йеппе, что указал на это. Кроме того, Lasse указывает, что даже без явного выхода за рамки переменные, которые больше не используются, будут иметь право на сборку мусора в сборках выпуска.

TL; DR хотя использование незапрошенной области может помочь передать логическое использование переменной области человеку, это имеет нет влияние на Ли объект получает право на сбор, в рамках того же метода.

i.e в приведенном ниже коде, определение области и даже повторное назначение переменной foo ниже не имеет совершенно никакого влияния на продолжительности жизни.

void MyMethod()
{
  // Gratuituous braces:
  {
      var foo = new ExpensiveClass();
  }
  {
      Bar foo = new Bar(); // Don't repurpose, it impairs readability!
  }
  Thread.Sleep(TimeSpan.FromSeconds(10));
  GC.Collect();
  GC.WaitForPendingFinalizers();
  <-- Point "X"
}

В Точке X:

  • на DEBUG построить, ни foo переменные будут собраны, несмотря на хакерские попытки соблазнить GC сделать это.
  • на RELEASE build, оба foos были бы право для собрания как только они не необходимы, независимо от объема. Сроки сбора, конечно, следует оставить на свое усмотрение.

этот шаблон практически не влияет на время выполнения C#, поэтому это чисто эстетическая вещь (сравните с C++, где мы регулярно используем этот шаблон с RAII для охвата таких вещей, как замки).

Если у меня есть два совершенно несвязанных блока кода, я иногда буду охватывать их таким образом, чтобы было на 100% ясно, какие переменные программист должен держать в голове как "потенциально измененные в предыдущем блоке. Он заполняет пробел между on-big-code-block и изолированными функциями; я могу поделитесь некоторыми переменными, а не другими.

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

когда я использую это, я стилистически люблю ставить комментарий перед каждым блоком, примерно где if оператор пойдет, объясняя, что будет делать блок. Я считаю, что полезно избегать других разработчиков, думающих: "похоже, раньше был поток управления, но кто-то все испортил.- В данном случае это может показаться излишним, но вы поймете:--3-->

// Add a menu item for Google, if value is high enough.
{
    string url = "#";

    if ( value > 5 )
        url = "http://google.com";

    menu.Add( new MenuItem(url) );
}

// Add a menu item for Cheese, if the value is high enough
{
    // cheese
    string url = "#";

    if ( value > 45 )
        url = "http://cheese.com";

    menu.Add( new MenuItem(url) );
}

как было сказано ранее, это чисто стилистическое в C#. Не стесняйтесь использовать свой собственный стиль, где это имеет смысл.


{
    string f = "hello";
}

просто выглядит странно.

очевидно, методы нуждаются в них:

private void hmm() {}

и Switch:

switch(msg)
{
    case "hi":
    // do something
    break;

    // ...
}

и даже если, для, foreach, while заявления...

if(a == 1)
{
    bool varIsEqual = true;
    isBusy = true;
    // do more work
}

но если у вас есть только 1 оператор в цикле или оператор if, вам не нужны фигурные скобки:

if("Samantha" != "Man")
    msg = "Phew!";