Создание экземпляров локальных переменных

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

static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}

Однако если перенести объявления x за пределы цикла, то экземпляр x будет создаваться только один раз.

static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}

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

Пример:

using System;

delegate void D();

class Test
{
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}

static void Main() {
foreach (D d in F()) d();
}
}

дает на выходе:

1
3
5

Однако при переносе объявления x за пределы цикла:

static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}

вывод имеет вид:

5
5
5

Если в цикле for объявляется переменная итерации, то сама эта переменная считается объявленной за пределами цикла. Таким образом, если изменить пример, чтобы захватывать саму переменную итерации

static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}

то будет захвачен только один экземпляр переменной итерации. В этом случае вывод имеет вид

3
3
3

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

static D[] F() {
D[] result = new D[3];
int x = 0;
for (int i = 0; i < 3; i++) {
int y = 0;
result[i] = () => { Console.WriteLine("{0} {1}", ++x, ++y); };
}
return result;
}

то три делегата будут захватывать один экземпляр x, но разные экземпляры y. Вывод будет иметь вид:

1 1
2 1
3 1

Отдельные анонимные функции могут захватывать один экземпляр внешней переменной. В примере:

using System;

delegate void Setter(int value);

delegate int Getter();

class Test
{
static void Main() {
int x = 0;
Setter s = (int value) => { x = value; };
Getter g = () => { return x; };
s(5);
Console.WriteLine(g());
s(10);
Console.WriteLine(g());
}
}

две анонимные функции захватывают один экземпляр локальной переменной x и поэтому они могут "взаимодействовать" через эту переменную. Вывод в данном примере имеет вид:

5
10

Вычисление выражений анонимных функций

Анонимная функция F всегда должна преобразовываться в тип делегата D или тип дерева выражения E либо напрямую, либо с помощью выполнения выражения создания делегата new D(F). Это преобразование определяет результат анонимной функции, как описано в разделе §6.5.

Выражения запросов

Выражения запросов представляют собой подмножество синтаксических конструкций языка для запросов, похожих запросы иерархических или реляционных языков запросов, таких как SQL и XQuery.

query-expression:
from-clause query-body

from-clause:
from typeopt identifier in expression

query-body:
query-body-clausesopt select-or-group-clause query-continuationopt

query-body-clauses:
query-body-clause
query-body-clauses query-body-clause

query-body-clause:
from-clause
let-clause
where-clause
join-clause
join-into-clause
orderby-clause

let-clause:
let identifier = expression

where-clause:
where boolean-expression

join-clause:
join typeopt identifier in expression on expression equals expression

join-into-clause:
join typeopt identifier in expression on expression equals expression into identifier

orderby-clause:
orderby orderings

orderings:
ordering
orderings , ordering

ordering:
expression ordering-directionopt

ordering-direction:
ascending
descending

select-or-group-clause:
select-clause
group-clause

select-clause:
select expression

group-clause:
group expression by expression

query-continuation:
into identifier query-body

Выражение запроса начинается с предложения from и заканчивается предложением select или group. После начального предложения from могут находиться (или отсутствовать) предложения from, let, where, join или orderby. Каждое предложение from является генератором, предлагающим переменную диапазона, которая включает элементы последовательности. Каждое предложение let вводит переменную диапазона, которая представляет значение, вычисляемое с помощью предыдущих переменных диапазона. Каждое предложение where является фильтром для исключения элементов из результата. Каждое предложение join сравнивает указанные ключи исходной последовательности с ключами другой последовательности, выдавая совпадающие пары. Каждое предложение orderby изменят порядок элементов в соответствии с указанными критериями. Конечное предложение select или group задает формат результата в виде переменных диапазона. И наконец, предложение into можно использовать для "склеивания" запросов, рассматривая результаты одного запроса в качестве генератора для последующего запроса.



Наши рекомендации