Как избежать захвата переменных в лямбда-выражении? - PullRequest
4 голосов
/ 07 апреля 2011


В последнее время я читал C # в глубине, и это научило меня лямбда-выражению, я использовал их для передачи данных, чтобы щелкнуть события и такие, как:

image.MouseDown+=(o ,e)=>MethodToDoSomething(DataNeededForAction);

теперь проблема с ними заключается в захвате переменных при использовании в цикле foreach (спасибо Джону Скиту (Jon Skeet) за то, что он действительно прояснил эту часть :), при инициализации нескольких объектов, на которые есть подписки, на которые я подписываюсь, я обычно сталкиваюсь с проблемой с переменной отлов. рассмотрим следующий пример:

foreach (var game in GamesCollection)
{
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(game.id);
}

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

Thx, Ziv

Ответы [ 4 ]

5 голосов
/ 07 апреля 2011

(РЕДАКТИРОВАТЬ: обратите внимание, что это изменилось в C # 5, где foreach теперь эффективно создает новую переменную для каждой итерации.)

Нет, избежать этого невозможно.По сути, это было ошибкой в ​​том, как спецификация языка описывала foreach в терминах одной переменной, а не новой переменной на итерацию.Лично я не рассматриваю это как проблему с точки зрения количества задействованного кода - к тому времени, как вы поняли, что это проблема, и выяснили, как ее исправить, вы преодолели самое большое препятствие.Заметим, что я обычно добавляю комментарий, чтобы сделать его понятным для программистов по техническому обслуживанию.

Я уверен, что команда C # сделала бы это по-другому, если бы начинала с нуля, но это не так.Черт, они даже обсуждали изменение существующего поведения ... но есть веские причины против этого изменения (особенно тот код, который работает корректно на компиляторе C # N + 1, все равнокомпилировать, но давать неверный результат на компиляторе C # N; очень тонкий источник ошибок для авторов библиотек с открытым исходным кодом, которые ожидают, что их код будет построен с использованием нескольких компиляторов).

(надеюсь, вам понравится книгаКстати ...)

2 голосов
/ 07 апреля 2011

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

0 голосов
/ 07 апреля 2011

Как насчет того, чтобы сначала вытащить game.id с выражением LINQ. Пересылка вызова через .Aggregate () должна каждый раз создавать новую переменную, потому что это действительно параметр метода.

GamesCollection.Select(game => game.id).Aggregate((_, id) => {
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(id);
    return img;
});
0 голосов
/ 07 апреля 2011

Если GamesCollection - это List<T>, вы можете использовать метод ForEach:

GamesCollection.ForEach(game => {
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(game.id);
});

Но, как правило, нет.И одна дополнительная переменная не так уж и загромождена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...