Объем переменных в С#

У меня вопрос для вас, ребята, написав код на С#: У меня были долгие дебаты с моим коллегой: Он говорит, что, объявив и инициализируя все переменные в начале функции, сделайте код более читаемым и быстрым.

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

1.. Он не читается, как если бы у вас была длинная функция, у вас должна быть огромная область объявления после области intialization, так что это намного проще объявить нужную вам переменную там, где вам это нужно, так что вы увидите это в вас.

2. Это не совсем быстрее, поскольку вы выделяете ненужную память и накапливаете стек функций.

Этот пример псевдокода:

public void A(double dParam) 
{         
    if(... condition ... ) {
        double dAnotherParam;
        string sParam; 
        ...
        // use local scope vars here
     }   
}

Пример псевдокода:

public void A(double dParam) 
{
    double dAnotherParam;
    string sParam; 

    dAnotherPatam = 0; 
    sParam = null;     

    if(... condition ... ) {
        ...
    }                   
 }

Любые идеи по теме?

Спасибо заранее.

Ответ 1

Это вопрос стиля. Мои соображения:

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

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

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

Например, код:

static void Main(string[] args)
{
    var a = 3;

    if (a > 1)
    {
        int b = 2;
        a += b;
    }

    var c = 10;
    Console.WriteLine(a + c);   
 }

Создает следующий IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       24 (0x18)
  .maxstack  2
  .locals init ([0] int32 a,     // Declare a
           [1] int32 b,          // Declare b
           [2] int32 c)          // Declare c
  IL_0000:  ldc.i4.3             
  IL_0001:  stloc.0              
  IL_0002:  ldloc.0              
  IL_0003:  ldc.i4.1             
  IL_0004:  ble.s      IL_000c
  IL_0006:  ldc.i4.2
  IL_0007:  stloc.1
  IL_0008:  ldloc.0
  IL_0009:  ldloc.1
  IL_000a:  add
  IL_000b:  stloc.0
  IL_000c:  ldc.i4.s   10
  IL_000e:  stloc.2
  IL_000f:  ldloc.0
  IL_0010:  ldloc.2
  IL_0011:  add
  IL_0012:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0017:  ret
} // end of method Program::Main

Важно отметить, что переменные во внутренних областях, такие как переменная b, которая находилась в области if, объявляются в области методов компилятором.

Надеюсь, что это поможет.

Ответ 2

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

В С# лучше разместить их в области, где они нужны. Это имеет несколько преимуществ, в том числе:

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

Последний момент, на мой взгляд, самый важный. Хорошая ремонтопригодность и тестируемость основываются на abiltiy, чтобы ваши методы были небольшими - и сохранение переменных, объявленных как они используются, делает извлечение метода намного проще. Не только инструменты становятся более эффективными, результирующие автоматические рефакторинги имеют тенденцию быть проще (поскольку переменная не передается ref и т.д.).


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

public void A(double dParam) 
{         
    if(... condition ... ) {
        double dAnotherParam = GetValueFromMethod();

        // use local scope vars here

        // Later, as needed:
        string sParam = dAnotherParam.ToString(); 
     }   
}

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

Ответ 3

Это довольно просто и понятно. Это правило - это то, что я использую в своем коде весь день каждый день.

Объявить переменные в самой низкой области, с которой вы можете обойтись.

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

Изменить: похоже, вы задаете вопрос о переменных области в методах.

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

Ответ 4

Объявление переменных в верхней части метода - очень старая школа и зависает от C. Мне преподавали то же самое в Университете.

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

Единственный раз, когда я хотел бы это сделать, - это когда вы хотите инициализировать что-то внутри оператора try catch finally, где вы хотите что-то сделать с объектом с блоком catch или finally.

SomeObject someObject = null;

try
{
    someObject = GetSomeObject();
    return someObject.DoSomething();
}
finally
{
    if (someObject != null) someObject.DoSomethingElse();
}

В большинстве случаев все, что вы хотите сделать, это очистить объект, поэтому оператор using более чем достаточен, но есть моменты, когда это необходимо.

Ответ 5

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

Кроме того, помещая переменные, близкие к используемому ими коду, он улучшает читаемость IMO.

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

Ответ 6

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

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

Я бы выбрал первый вариант.

Ответ 7

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

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

.MODEL SMALL

.DATA 
    myVariable1 DW "This is my first variable"
    myVariable2 DB "Second var"

.CODE 'code segment

:ProgramStart

    MOV AX,01h
    ...

:ProgramEnd

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

Однако с появлением ООП и программирования событий мы стараемся как можно ближе к событию. То, что я имею в виду, - это ваша точка зрения. Сегодня компиляторы анализируют код для таких переменных во всем коде и делают его управляемым кодом CLR. Так что это уже не имеет значения, говоря о производительности. В сегодняшнем программировании мы стараемся сделать код максимально понятным и понятным. Тем не менее, это включает объявления переменных.

Объявление переменной в начале функции и использование ее только 20 строк дальше нецелесообразно, плюс она, как правило, затрудняет понимание программы. Например, вы анализируете фрагмент кода, а затем прыгаете! появляется имя переменной, но без объявления. Откуда возникает эта переменная, спросите вы? Вам нужно будет прокрутить вверх (или перейти к определению), чтобы узнать, что это за переменная. Между тем, возможно, вы потеряли свое представление о цели рутины, которую вы действительно читали и пытались понять. Сделайте это три или более раз, и ваш потерянный пытается понять код.

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

Это мои два цента. Я надеюсь быть достаточно ясным в том, что я пытался объяснить.

Я надеюсь, что это все равно поможет!

Ответ 8

объявить переменные в области, которую вы намереваетесь использовать.

Ответ 9

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

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

Ответ 10

Re: читаемость, вы совершенно правы. А также подумайте о том, что объявление переменных в более высоком пространстве теряет информацию о том, где они имеют отношение. Пользователь, просматривающий его, должен прочитать весь метод, чтобы узнать, где используется переменная. Более того, существует риск ошибочного использования неправильной переменной.

Ответ 11

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

Ответ 12

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

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

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

В идеале вы этого не делаете. Если какой-либо ветки вашего кода нужно выделить объекты, ортогональные остальной части функции, лучше разгрузить эту ветвь в отдельную функцию.

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

Ответ 13

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

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

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

Аргумент о том, что он быстрее, также неверен. Не имеет значения, где в методе, объявленном переменной, он все равно занимает столько же места в стеке. Таким образом, нет никаких принципиальных различий в производительности, которые я знаю о 1. Однако вы можете сделать аргумент о том, что объявления с встроенными инициализациями будут потреблять дополнительные циклы ЦП, если инициализация считается ненужной. Например, вы можете объявить и инициализировать переменную внутри блока if, и в этом случае инициализация произойдет только в том случае, если выполнение будет погружено в этот блок. Сравните это с тем, что произойдет, если вы выполнили декларацию и инициализацию в верхней части метода.

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

Ответ 14

Правило большого пальца, которое я всегда выполнял, заключается в том, чтобы заполнить переменные как можно ближе к их объявлению, это означает, что не объявлять переменную в строке 10, а затем давать ей начальное значение в строке 100.

Ответ 15

Паскаль был одним из более поздних языков, которые я узнал, где все должно было быть объявлено заранее в соответствии с вашим примером для друзей. Простите нас, более ранние языки не разрешали переменные с близкими возможностями à la.NET, поэтому "старые привычки умирают тяжело".

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

Для сравнения:

Dim X as integer = 0
' 100 lines of drivel, where I'm forgetting about X
For X = 1 To 10
  If pornographic(X) then ' Pron integers, geek alert!
    Viagra(X)
  End if
Next
' 50 lines of drivel, X lurks with his raincoat

If X >= 10 Then ' Compiles. X=11, if you're damn lucky
  ' make a nasty mistake
End if

Не смейтесь, это происходит каждый день.

С

' 100 lines of drivel, nothing lurid in sight
For X As Integer = 1 To 10
  If pornographic(X) then ' Pron integers, geek alert!
    Viagra(X)
  End if
Next
' 50 lines of drivel, X is no more

If X >= 10 Then ' *** FAILS AT COMPILE-TIME ***
  ' *** CAN'T *** make a nasty mistake
End if

Легкий ответ, правильно?

Maurice

P.S. Я забыл объявить: Public Protected Viagra (clipno As Film)...