metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-08-14 06:02 pm

C# и глюки с замыканиями

Я где-то сошел с ума, но с ходу не соображу где.

Есть такой код с замыканиями:

  class Program {

    delegate void ZeroArityFunc();

    static string[] testData=new string[] { "a", "b", "c", "d", "e", "f" };

    static void Main(string[] args) {
      List list = new List();
      foreach(string s in testData) {
        ZeroArityFunc f = () => Console.Write(s); //создаем лямбду и присваиваем ее f
        list.Add(f);
      }

      foreach(ZeroArityFunc a in list) {
        a();
      }

    }
  }



Т.е. я пытаюсь сделать следующее: создать список функций без параметров, которые при вызове будут выдавать результаты от a до f. Это вообще имитация другого, рабочего кода, но суть та же самая.

А теперь внимание, ШИЗА: этот код выдает "f f f f f f", а не то, что я от него хочу ("a b c d e f"). Т.е. делегаты в списке каким-то чудесным образом начинают ссылаться не 6 разных экземпляров замыканий, а на один.

Что-то мне это начинает необъяснимо напоминать недавний пост [livejournal.com profile] alexclear про JavaScript, я сейчас для смеха попробую тамошний workaround.

PS: Жопа, workaround оттуда сработал. В кошмарном сне бы такое не привиделось, что непонятный пост про совершенно неизвестный мне язык через неделю окажется пригодным в работе.

PPS: Оказывается, известная шиза - переменная цикла s объявлена один раз и до цикла, и замыкается оно на нее.

[identity profile] skiminog.livejournal.com 2010-08-14 06:15 pm (UTC)(link)
Нельзя замыкаться на переменную цикла. Это же Боян из Боянов.
http://habrahabr.ru/blogs/net/36601/

[identity profile] gds.livejournal.com 2010-08-14 06:17 pm (UTC)(link)

(Anonymous) 2010-08-14 06:19 pm (UTC)(link)
Все тут хорошо. Замыкние указывает на переменую S, но во время вызона замыкание S указывает уже на последний делегат (во всех делегатах это одна и таже S).

Этот случай даже был описан в Этюдах на C# от Эрика Липперта:
http://habrahabr.ru/blogs/net/98779/

[identity profile] zamotivator.livejournal.com 2010-08-14 06:41 pm (UTC)(link)
Ты замыкаешь бокс, а не копию переменной.

[identity profile] honeyman.livejournal.com 2010-08-14 06:45 pm (UTC)(link)
Гы, вот ровно вчера то же самое писал, но про Python (http://habrahabr.ru/blogs/crazydev/101715/#comment_3154402).

[identity profile] max630.livejournal.com 2010-08-14 07:34 pm (UTC)(link)
afair java при засовывании переменной в анонимный класс требует чтобы она была final. Причём это чуть ли не error.

[identity profile] max630.livejournal.com 2010-08-14 07:36 pm (UTC)(link)
так что можно попробовать обойтись простым "s2 = s;", и его уже засовывать.

[identity profile] alpha-cygnus.livejournal.com 2010-08-14 07:38 pm (UTC)(link)
Do not use mutability ;-)
Замыкается переменная, а не значение. А потом она используется, одна и та же на все замыкания. Замыканий-то много, а переменная в них одна.

[identity profile] w00dy.livejournal.com 2010-08-14 07:44 pm (UTC)(link)
ребе, вас var и System.Action пользовать не учили?

[identity profile] alexandr0.livejournal.com 2010-08-14 07:47 pm (UTC)(link)
Ну так мутабельность как бы намекает, что следует быть настороже :)

[identity profile] hshhhhh.livejournal.com 2010-08-14 09:44 pm (UTC)(link)
поиграл я в ваш UT.
компьютер у меня дикое говно, тормозит безбожно, но тем не менее суть игры понятна: UT прекрасный заменитель контре, но вот warsow прекрасный заменитель кваке, подинамичнее так. обе хорошие.

[identity profile] avr-forever.livejournal.com 2010-08-15 12:58 pm (UTC)(link)
А вот писали бы Вы, ребе, на тикле, проблем бы таких не было. Там чёткий порядок подстановок, потому всегда ясно, будет использоваться переменная либо её значение.