[Этюд] Понимаете ли вы замыкания?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 09.12.07 10:59
Оценка: 68 (11)
Что выведет каждая из следующих программ на C#?

///////////////////////////////////
// Вариант 1.
///////////////////////////////////
using System;
using System.Collections.Generic;

delegate int F();

class Program
{

   static void Main()
   {
       List<F> fs = new List<F>();
       int[] xs = {1, 2, 3};

       foreach (int x in xs)
       {
           fs.Add(delegate { return x; });
       }

       foreach (F f in fs)
       {
           Console.WriteLine(f());
       }
   }
}


///////////////////////////////////
// Вариант 2.
///////////////////////////////////
using System;
using System.Collections.Generic;

delegate int F();

class Program
{

   static void Main()
   {
       List<F> fs = new List<F>();
       int[] xs = {1, 2, 3};

       foreach (int x in xs)
       {
           int y = x;
           fs.Add(delegate { return y; });
       }

       foreach (F f in fs)
       {
           Console.WriteLine(f());
       }
   }
}


///////////////////////////////////
// Вариант 3.
///////////////////////////////////
using System;
using System.Collections.Generic;

delegate int F();

class Program
{

   static void Main()
   {
       List<F> fs = new List<F>();
       int[] xs = {1, 2, 3};

       foreach (int x in xs)
       {
           fs.Add(delegate
                      {
                          int y = x;
                          return y;
                      });
       }

       foreach (F f in fs)
       {
           Console.WriteLine(f());
       }
   }
}


///////////////////////////////////
// Вариант 4.
///////////////////////////////////
using System;
using System.Collections.Generic;

delegate int F();

class Program
{
   static void Main()
   {
       List<F> fs = new List<F>();
       int[] xs = {1, 2, 3};

       for (int i = 0; i < 3; i++) // Превед, Numnul ;-)
       {
           fs.Add(delegate { return xs[i]; });
       }

       foreach (F f in fs)
       {
           Console.WriteLine(f());
       }
   }
}


Проверить свой ответ можно, скомпилировав и запустив их.
Re: [Этюд] Понимаете ли вы замыкания?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 09.12.07 11:28
Оценка:
Здравствуйте, nikov, Вы писали:

N>Что выведет каждая из следующих программ на C#?


Попробую.... (не запуская)

1) 3 3 3
2
) 1 2 3
3
) 3 3 3
4
) exception
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re: [Этюд] Понимаете ли вы замыкания?
От: desco США http://v2matveev.blogspot.com
Дата: 09.12.07 12:04
Оценка:
Здравствуйте, nikov, Вы писали:

N>Что выведет каждая из следующих программ на C#?


N>
N>///////////////////////////////////
N>// Вариант 1.
N>///////////////////////////////////
N>


3 3 3

N>
N>///////////////////////////////////
N>// Вариант 2.
N>///////////////////////////////////
N>


1 2 3

N>
N>///////////////////////////////////
N>// Вариант 3.
N>///////////////////////////////////
N>


3 3 3

N>
N>///////////////////////////////////
N>// Вариант 4.
N>///////////////////////////////////
N>


IndexOutOfRangeException
Re: [Этюд] Понимаете ли вы замыкания?
От: orangy Россия
Дата: 09.12.07 13:57
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>Что выведет каждая из следующих программ на C#?

Вот если бы придумать разумные соображения и выдавать предупреждения в ReSharper-е. Момент-то тонкий, и далеко не всем понятный. Да и случайно легко ошибиться в чуть более сложном варианте. Я пытался, но мне не удалось изобрести некую метрику, которая выдавала бы не слишком много ложных срабатываний.
... << RSDN@Home 1.2.0 alpha rev. 655>>
"Develop with pleasure!"
Re[2]: [Этюд] Понимаете ли вы замыкания?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 09.12.07 22:22
Оценка: 29 (3) +2
Здравствуйте, orangy, Вы писали:

O>Вот если бы придумать разумные соображения и выдавать предупреждения в ReSharper-е.


В качестве первого приближения — access to loop control variable в теле анонимного метода....
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re: [Этюд] Понимаете ли вы замыкания?
От: Аноним  
Дата: 10.12.07 09:59
Оценка: 1 (1)
Здравствуйте, nikov, Вы писали:

N>Что выведет каждая из следующих программ на C#?


Неужели только я не понимаю, чем первый вариант от второго отличается?
Re[2]: [Этюд] Понимаете ли вы замыкания?
От: B0rG  
Дата: 10.12.07 10:19
Оценка:
Здравствуйте, Аноним, Вы писали:

N>>Что выведет каждая из следующих программ на C#?

А>Неужели только я не понимаю, чем первый вариант от второго отличается?

Можно погадать, конечно, но лучше бы объяснили....

Во втором случае явно происходит копирование значения, но вот почему 1 и 3 показывают 333 я не понимаю...
Понимаете ли вы замыкания?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 10.12.07 10:37
Оценка: 65 (9)
#Имя: FAQ.cs.closure
Здравствуйте, Аноним, Вы писали:

А>Неужели только я не понимаю, чем первый вариант от второго отличается?


Замыкания реализуются с помощью нескольких compiler-generated классов (в простейших случаях может не понадобиться создавать ни одного класса). Каждый класс соответствует блоку, ограниченному фигурными скобками. Локальные переменные, объявленные внутри блока, становятся полями compiler-generated класса. Важно понимать, что в замыкание захватываются не значения переменных, а сами переменные (значения которых могут измениться к тому моменту, когда они будут использованы). Экземпляр класса, соответствующий внутреннему блоку, хранит ссылку на экземпляр класса, соответствующий внешнему блоку (6.5.3 Implementation example). Каждый раз, когда поток управления входит внутрь блока, создается новый экземпляр соответствующего compiler-generated класса. Этот процесс известен как instantiation of local variables (7.14.4.2 Instantiation of local variables). Когда поток управления покидает блок, экземпляр соответствующего compiler-generated класса может продолжить свое существование, если сохраняются ссылки на делегат, привязанный к такому экземпляру (то есть, делегат, созданный из анонимного метода, объявленного внутри соответствующего блока). Таким образом, даже при наличии только одного потока, одновременно могут существовать несколько экземпляров одного compiler-generated класса, хранящего несколько разных наборов локальных переменных. Если локальные переменные объявлены в различных, вложенных один в другой блоках, то нескольким наборам переменных из внутреннего блока может соответствовать один набор переменных внешнего блока. В приведенных примерах как раз обыгрывается такая ситуация. Переменная-итератор в циклах for и foreach считается объявленной вне блока, являющегося телом цикла, поэтому она инстанциируется всего один раз, независимо от количества итераций. На каждой итерации изменяется значение одного и того же экземпляра этой переменной. Если же мы перекладываем ее значение в другую переменную, объявленную внутри блока, то на каждой итерации создается отдельный экземпляр этой переменной, который не модифицируется при следующих итерациях.

P.S. Ссылки в скобках даны на номера параграфов C# 3.0 Specification Draft.
Re: [Этюд] Понимаете ли вы замыкания?
От: meerius Канада  
Дата: 10.12.07 11:32
Оценка:
Здравствуйте, nikov

Скажите пожалуйста, такой вот вопрос имеет место на собеседование, ну, всмысле, разумно ли его задавать собеседовав кандидата.
Меня интересует лично Ваше мнение, и если Вам не трудно, то почему Да или Нет?
Спасибо.
«Мы с тобой в чудеса не верим, Оттого их у нас не бывает…»
Re[2]: [Этюд] Понимаете ли вы замыкания?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 10.12.07 11:43
Оценка: 2 (2) +4
Здравствуйте, meerius, Вы писали:

M>Скажите пожалуйста, такой вот вопрос имеет место на собеседование, ну, всмысле, разумно ли его задавать собеседовав кандидата.

M>Меня интересует лично Ваше мнение, и если Вам не трудно, то почему Да или Нет?

Имхо, общее понимание того, что такое замыкания и как они себя ведут, очень полезно для программиста на C# 3.0 (особенно учитывая наметившийся уклон в ФП), и это можно спрашивать на собеседованиях. Обыгранные в этом этюде вопросы: то что, переменная цикла считается определенной вне тела цикла, и вопросы поведения mutable переменных в замыканиях — это уже достаточно тонкие вещи, которые отличаются от языка к языку(*), и, мне кажется, их имеет смысл спрашивать, только если Вы набираете людей для разработки компилятора или вроде того.


(*) Например, в Nemerle переменная цикла foreach инстанциируется на кажой итерации.
Re: [Этюд] Понимаете ли вы замыкания?
От: vdimas Россия  
Дата: 10.12.07 11:55
Оценка:
Здравствуйте, nikov, Вы писали:

Отличный пример кстати, меня это раздражало в C# 2.0 с самого начала, о чём писал уже, называя их "полузамыканиями". Интуитивно программисты ждут от переменной цикла foreach "локального" поведения, как если бы был такой код:
for(IEnumerator<int> en = xs.GetEnumerator(); en.MoveNext();) {
    int x = en.Current;
}
... << RSDN@Home 1.2.0 alpha rev. 786>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.