#c_sharp
Есть например вот такая функция void Foo (int х = 23) { Console.WriteLine (х); } который имеет необязательный параметр x. Я всегда читал, что для параметров и локальных переменных в функции начисляется память, то есть строится так называемый stack тогда, когда вызывается функция. Объясните, в таком случае, где хранится значение переменной x.
Ответы
Ответ 1
Смотрите, всё просто. На самом деле, необязательные параметры обрабатываются не на уровне вызываемой функции, а на уровне вызывающей. Что это означает? А вот что. Для объявления void Foo(int х = 23) { Console.WriteLine(х); } компилятор не генерирует две функции: void Foo(int х) { Console.WriteLine(х); } void Foo() { int х = 23; Console.WriteLine(х); } Он в реальности генерирует лишь одну функцию void Foo(int х) { Console.WriteLine(х); } А вот вызов Foo() при этом компилятором переписывается так, как будто вы написали Foo(23)! Таким образом, константа 23 вставляется в вызывающий код на этапе компиляции. И хранится он там же, где и хранилась бы при явном вызове Foo(23). Отсюда такое следствие. Допустим, ваша функция Foo определена в сборке A, а сборка B вызывает Foo(). Затем, вы меняете в исходнике значение по умолчанию с 23 на 0, и компилируете снова сборку A, но не сборку B. Поскольку константа 23 «вкомпилирована» в сборку B, то при запуске функция Foo будет фактически с параметром x = 23, а не 0. Источник информации: Optional argument corner cases, part three.Ответ 2
Стек создается не при вызрве функции, а при создании потока исполнения (execution thread). При вызове функции в нем выделяется и инициализируется память под локальные переменные и формальные аргументы (иногда это делается через регистры, но IL умеет только через стек). Стек потока - это область памяти с механизмом доступа. Значением необязательного параметра всегда является констата (значение, вычисляемое при компиляции), поэтому наверняка для CLR она хранится там же, где и остальные константы.Ответ 3
Хочу добавить к ответу цитату из Рихтера clr via c# При вызове метода извне модуля изменение значения параметров по умолчанию является потенциально опасным. Вызывающая сторона использует значение по умолчанию в процессе работы. Если изменить его и не перекомпилировать код, содержащий вызов, в вызываемый метод будет передано прежнее значение. В качестве индикатора поведения можно использовать значение по умолчанию 0 или null. В результате исчезает необходимость повторной компиляции кода вызывающей стороны. Вот пример: // Не делайте так: private static String MakePath(String filename = "Untitled") { return String.Format(@"C:\{0}.txt", filename); } // Используйте следующее решение: private static String MakePath(String filename = null) { // Здесь применяется оператор, поддерживающий // значение null (??); return String.Format(@"C:\{0}.txt", filename ?? "Untitled"); }
Комментариев нет:
Отправить комментарий