Краткий ответ: нет. .NET не нужно идти по цепочке областей видимости, чтобы найти переменные.
Длинный ответ:
Начните с этого примера:
static Func<string> CaptureArgs(int a, int b)
{
return () => String.Format("a = {0}, b = {1}", a, b);
}
static void Main(string[] args)
{
Func<string> f = CaptureArgs(5, 10);
Console.WriteLine("f(): {0}", f());
// prints f(): a = 5, b = 10
}
В методе CaptureArgs
в стеке существуют a
и b
. Интуитивно понятно, что если мы ссылаемся на переменные в анонимной функции, возвращаем функцию и извлечение кадра стека должно удалить a
и b
из памяти. (Это называется проблема вверху funargs ).
C # не страдает от проблемы восходящих funargs, потому что за кулисами анонимная функция является просто причудливым синтаксическим сахаром по сравнению с классом, сгенерированным компилятором. Код C # выше превращается в:
private sealed class <>c__DisplayClass1
{
// Fields
public int a;
public int b;
// Methods
public string <CaptureArgs>b__0()
{
return string.Format("a = {0}, b = {1}", this.a, this.b);
}
}
Компилятор создает и возвращает новый экземпляр <>c__DisplayClass1
, инициализирует его поля a
и b
из a
и b
, переданных в метод CaptureArgs
(это фактически копирует a
и b
из стека в поля, существующие в куче), и возвращает его вызывающей стороне. Вызов f()
- это действительно звонок <>c__DisplayClass1.<CaptureArgs>b__0()
.
Поскольку a
и b
, на которые есть ссылка в <CaptureArgs>b__0
, являются полями ванили, на них может ссылаться делегат напрямую, им не требуются какие-либо особые правила объединения областей.