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

Явные локальные области - любая истинная выгода?

Я очистил код и удалил оператор if, который больше не нужен. Однако я понял, что забыл снять скобки. Это, конечно, действительно и просто создало новую локальную область. Теперь это заставило меня задуматься. За все годы разработки С# я никогда не сталкивался с какой-либо причиной их использования. Фактически, я как бы забыл, что могу это сделать.

Есть ли реальная польза для определения локальной области? Я понимаю, что я могу определить переменные в одной области, а затем определить те же самые снова в несвязанной области (для, foreach и т.д.), Как показано ниже:

void SomeMethod()
{    
    {
        int i = 20;
    }

    int i = 50; //Invalid due to i already being used above.
}

void SomeMethod2()
{    
    {
        int i = 20;
    }
    {
        int i = 50; //Valid due to scopes being unrelated.
    }
    {
        string i = "ABCDEF";
    }
}

Какова истинная точка определения локальной области? Может ли быть какой-то прирост производительности (или, возможно, потеря)? Я знаю, что вы могли бы сделать это на С++ и помогали вам управлять памятью, но поскольку это .NET, действительно ли была бы польза? Является ли это всего лишь би-продуктом языка, который позволяет нам определять случайные области охвата, даже если нет истинной выгоды?

4b9b3361

Ответ 1

В С# это чисто синтаксис, чтобы превратить группу операторов в один оператор. Требуется для любого ключевого слова, которое ожидает, что будет следовать один оператор, например, если, для, использования и т.д. Несколько угловых случаев:

  • ключевое слово case внутри коммутатора является особенным, поскольку оно не требует, чтобы он был одним из операторов. Ключевое слово break или goto заканчивает его. Это объясняет, почему вы можете использовать фигурные скобки для замятия в объявлении переменной.
  • ключевые слова try и catch являются особыми, они требуют скобок, даже если следует только один оператор. Довольно необычный, но, вероятно, вдохновленный тем, что программист задумывается над областью деклараций внутри блоков, блок catch не может ссылаться на переменные внутри блока try из-за того, как работает обработка исключений.

Ограничение объема локальных переменных с ним является потерянной причиной. Это большое дело в С++, потому что конечная скобка - это место, где компилятор будет вводить вызовы деструктора для переменных внутри блока области видимости. Это ab/используется все время для шаблона RAII, ничего страшного в том, что препинание в программе имеет такие резкие побочные эффекты.

Команда С# не имела большого выбора в этом отношении, время жизни локальных переменных строго контролируется дрожанием. Который не обращает внимания на какие-либо группирующие конструкции внутри метода, он знает только об ИЛ. Который не имеет никаких конструкций группировки, кроме try/except/finally. Объем любой локальной переменной, независимо от того, где она была написана, является телом метода. Что-то, что вы можете увидеть при запуске ildasm.exe на скомпилированном С# -кодеке, вы увидите, что локальные переменные подняты в начало тела метода. Которая частично также объясняет, почему компилятор С# не позволит вам объявить другую локальную переменную в другом блоке области с тем же именем.

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

Это делает его очень эффективным при сборе объектов. Немного слишком эффективный иногда и хлопотный, когда вы взаимодействуете с собственным кодом, вам может понадобиться магический метод GC.KeepAlive() для продления срока службы. Очень замечательный метод, он вообще не генерирует никакого кода. Его единственное использование - заставить дрожание изменить таблицу и вставить больший адрес для переменной времени жизни.

Ответ 2

Подобно функциям, эти "блоки" в значительной степени просто изолируют области (в основном) несвязанного кода и их локальные переменные внутри функции.

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

int Foo(int a) {

    // ...

    {
        int temp;
        SomeFuncWithOutParam(a, out temp);

        NowUseThatTempJustOnce(temp);            
    }

    MistakenlyTryToUse(temp);    // Doesn't compile!

    // ...

}

Можно, однако, сказать, что если вам нужна такая лексическая область обзора, внутренние блоки должны быть функциями в любом случае.

Что касается производительности и т.д., я очень сомневаюсь, что это важно. Компилятор рассматривает функцию в целом и собирает все локальные переменные (даже объявленные в строке) при определении размера фрейма стека. Таким образом, в любом случае все локальные переменные объединяются. Это чисто лексическая вещь, которая даст вам немного больше ограничений на использование ваших переменных.

Ответ 3

Есть место, где могут быть полезны локальные области: внутри case операторов оператора switch. Все случаи делят по умолчанию ту же область действия, что и оператор switch. Объявление локальной временной переменной с таким же именем внутри нескольких операторов case недопустимо, и вы можете в конечном итоге объявить переменную только в первом case-statement или даже вне switch -statement. Вы можете решить эту проблему, предоставив каждому аргументу case локальную область действия и объявив временную переменную внутри области. Однако не делайте ваши дела слишком сложными, эта проблема может быть признаком того, что лучше вызвать отдельный метод для обработки case -statement.

Ответ 4

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

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

Нет реального выигрыша, запрещающего блоки операторов, используемые в вашем примере. Иногда они полезны, чтобы избежать конфликтов имен, но вы могли бы решить свою проблему без этого. Это также внесло бы различие в правилах синтаксиса по сравнению с такими языками, как C, С++ и Java.

В случае, если вам интересно, это также не изменяет время жизни объектов ссылочных объектов.

Ответ 5

Не будет увеличения производительности, по крайней мере, в режиме выпуска: GC может собирать объект, если он знает или, по крайней мере, думает, что он больше не будет использоваться, независимо от области действия. Это может привести вас к неуправляемому взаимодействию: http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx.

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