Каково значение анонимного неприкрепленного блока в C#?
В C# вы можете создать блок внутри метода, который не привязан ни к какому другому выражению.
public void TestMethod()
{
{
string x = "test";
string y = x;
{
int z = 42;
int zz = z;
}
}
}
Этот код компилируется и выполняется так, как если бы внутри метода main не было фигурных скобок. Также обратите внимание на блок внутри блока.
Есть ли сценарий, где это будет ценно? Я еще не нашел, но мне любопытно услышать о находках других людей.
10 ответов
Область действия и сборка мусора. Когда вы покидаете неприкрепленный блок, все объявленные в нем переменные выходят из области видимости. Это позволяет сборщику мусора очистить эти объекты.
Ray Hayes отмечает, что сборщик мусора в.NET не будет сразу собирать объекты вне области видимости, поэтому основным преимуществом является определение области видимости.
Например, если вы хотите повторно использовать имя переменной, обычно вы не можете повторно использовать имена переменных. Это недопустимо.
int a = 10;
Console.WriteLine(a);
int a = 20;
Console.WriteLine(a);
но это:
{
int a = 10;
Console.WriteLine(a);
}
{
int a = 20;
Console.WriteLine(a);
}
Единственное, о чем я могу думать сейчас, это, например, если вы обрабатывали какой-то большой объект и извлекли из него некоторую информацию, а после этого вы собирались выполнить кучу операций, вы могли бы поместить обработку большого объекта в блоке, так что он выходит из области видимости, а затем продолжить с другими операциями
{
//Process a large object and extract some data
}
//large object is out of scope here and will be garbage collected,
//you can now perform other operations with the extracted data that can take a long time,
//without holding the large object in memory
//do processing with extracted data
Это побочный продукт правила синтаксического анализа, что оператор является либо простым оператором, либо блоком. т. е. блок может использоваться везде, где может быть один оператор.
например
if (someCondition)
SimpleStatement();
if (SomeCondition)
{
BlockOfStatements();
}
Другие отметили, что объявления переменных находятся в области действия до конца содержащего блока. Для временных переменных хорошо иметь короткую область видимости, но мне никогда не приходилось использовать отдельный блок для ограничения области видимости переменной. Иногда для этого вы используете блок под оператором "using".
Так что в целом это не ценно.
Одна практическая причина его существования заключается в том, что вы хотите ограничить область действия некоторой переменной, когда нет острой необходимости вводить какие-либо другие причины для блока. На практике это практически никогда не бывает полезным.
Лично я предполагаю, что с точки зрения языка / компилятора проще сказать, что вы можете поместить блок в любое место, где ожидается выражение, и они просто не старались помешать вам использовать его без if. / для / объявление метода / и т. д.
Рассмотрим начало этого недавнего сообщения в блоге Эрика Липперта. if
за оператором не следует ни одного оператора, ни нескольких операторов, заключенных в фигурные скобки, за ним просто следует один оператор. Каждый раз, когда вы заключаете от 0 до N операторов в фигурные скобки, вы делаете этот раздел кода эквивалентным (с точки зрения синтаксического анализатора языка) одному выражению. Эта же практика применима и ко всем зацикленным структурам, хотя, как объясняет основной смысл поста в блоге, она не применяется к блокам try / catch / finally.
При обращении к блокам с этой точки зрения возникает вопрос: "Есть ли веская причина, чтобы запретить использование блоков везде, где можно использовать один оператор?" И ответ нет".
Это позволяет вам создать блок области видимости где угодно. Сам по себе он не так полезен, но может упростить логику:
switch( value )
{
case const1:
int i = GetValueSomeHow();
//do something
return i.ToString();
case const2:
int i = GetADifferentValue();
//this will throw an exception - i is already declared
...
В C# мы можем использовать блок области действия, так что элементы, объявленные в каждом случае, находятся только в области действия в этом случае:
switch( value )
{
case const1:
{
int i = GetValueSomeHow();
//do something
return i.ToString();
}
case const2:
{
int i = GetADifferentValue();
//no exception now
return SomeFunctionOfInt( i );
}
...
Это также может работать для goto и меток, но вы не часто используете их в C#.
Насколько я вижу, это было бы полезно только с организационной точки зрения. Я не могу себе представить какую-либо логическую ценность в этом. Возможно, у кого-то будет хороший пример.
Одна из причин для этого состоит в том, что переменные 'z' и 'zz' не будут доступны для кода ниже конца этого внутреннего блока. Когда вы делаете это в Java, JVM добавляет кадр стека для внутреннего кода, и эти значения могут жить в стеке. Когда код выходит из блока, кадр стека выталкивается, и эти значения исчезают. В зависимости от используемых типов это может избавить вас от необходимости использовать кучу и / или сборщик мусора.
Даже если бы это было действительно полезно по какой-либо причине (например, управление переменной областью), я бы отговорил вас от такой конструкции с точки зрения хорошей читаемости старого кода.
В C# - как c/ C++/java - фигурные скобки обозначают область видимости. Это диктует время жизни переменной. Когда закрывающая скобка достигнута, переменная сразу становится доступной для сборки мусора. В C++ это вызвало бы вызов деструктора класса, если бы var представлял экземпляр.
Что касается использования, единственно возможное использование - освободить большой объект, но tbh, установив его в null, будет иметь тот же эффект. Я подозреваю, что первое использование, вероятно, просто для того, чтобы программисты на С ++ переходили к управляемому коду на знакомой и удобной территории. Если вы действительно хотите вызвать "деструктор" в C#, вы обычно реализуете интерфейс IDisposable и используете шаблон "using (var) {...}".
Oisin
Это не имеет никакого значения, кроме семантического, а также для области и сборки мусора, ни одно из которых не имеет значения в этом ограниченном примере. Если вы думаете, что это делает код более понятным для вас и / или других, то вы наверняка можете использовать его. Однако, более приемлемое соглашение для семантического разъяснения в коде обычно будет использовать разрывы строк только с опциональными комментариями:
public void TestMethod()
{
//do something with some strings
string x = "test";
string y = x;
//do something else with some ints
int z = 42;
int zz = z;
}