Есть например вот такая функция
void Foo (int х = 23)
{
Console.WriteLine (х);
}
который имеет необязательный параметр x
. Я всегда читал, что для параметров и локальных переменных в функции начисляется память, то есть строится так называемый stack
тогда, когда вызывается функция.
Объясните, в таком случае, где хранится значение переменной x
.
Стек создается не при вызрве функции, а при создании потока исполнения (execution thread). При вызове функции в нем выделяется и инициализируется память под локальные переменные и формальные аргументы (иногда это делается через регистры, но IL умеет только через стек).
Стек потока - это область памяти с механизмом доступа. Значением необязательного параметра всегда является констата (значение, вычисляемое при компиляции), поэтому наверняка для CLR она хранится там же, где и остальные константы.
Смотрите, всё просто. На самом деле, необязательные параметры обрабатываются не на уровне вызываемой функции, а на уровне вызывающей.
Что это означает? А вот что. Для объявления
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.
Хочу добавить к ответу цитату из Рихтера 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");
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Здравствуйте, есть формула Беллара которая должна возвращать n-й разряд числа пи в двоичном представлении:
При правом клике мышки узел должен выделяться как и при левом клике
Это событие возвращает object sender и MouseButtonEventArgs eЧерез e