Создание экземпляров локальных переменных
Считается, что для локальной переменной создается экземпляр, когда выполнение входит в область действия переменной. Например, при вызове следующего метода экземпляр локальной переменной 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 можно использовать для "склеивания" запросов, рассматривая результаты одного запроса в качестве генератора для последующего запроса.