Аннотация:
В статье рассказывается внутренняя реализация замыканий (closure) в языке C# и описываются основные подводные камни, с которыми может столкнуться разработчик в своей повседневной деятельности.
Здравствуйте, Тепляков Сергей Владимирович, Вы писали:
Замыкания на переменные цикла зачастую приводят к очень неприятным последствиям, поскольку в этом случае поведение кода начинает отличаться от интуитивно понятного. Данный результат обусловлен двумя причинами: (1) при замыканиях осуществляется захват переменных, а не значений переменных, и (2) в приведенном фрагменте кода, существует один экземпляр переменной i, который изменяется на каждой итерации цикла, а не создается новый экземпляр на каждой итерации. Сложив эти два пункта вместе, мы получим, что будет создан только один объект генерируемого компилятором типа
Дедуктивный метод тут не уместен, поведение просто описано в стандарте, а его логичность или не логичность не может служить докаательством.
В определении Замыкания (closures) представляют собой фрагменты (блоки) кода, который можно использовать в качестве аргументов функций и методов.
ощущается несогласованность сложноподчиненного предложения: Замыкания ... фрагменты..., которЫЙ ...., возможно имелось в виду "которые".
И неплохо бы в последние примеры добавить примерную интерпритацию кода компилятором, чтобы было наглядно.
ТСВ>Авторы: ТСВ> Тепляков Сергей Владимирович
ТСВ>Аннотация: ТСВ>В статье рассказывается внутренняя реализация замыканий (closure) в языке C# и описываются основные подводные камни, с которыми может столкнуться разработчик в своей повседневной деятельности.
Честно говоря, статья не очень понравилась. Возможно, потому что я искал не совсем то, что нашел. Я бы сказал, отсутствует общий подход к рассмотрению проблемы, хотя если рассматривать данную статью чисто как знакомство с замыканиями, то пожалуй она имеет место.
Замыкания делаются на внешний контекст, и этим контекстом могут быть не только локальные переменные и аргументы. На самом деле замыкания можно делать на поля (статические и нестатические, в этом случае вместо класса просто создается метод статический или нестатический) и на this (автор говорит, что можно этот случай рассматривать как локальную переменную — абсолютная неправда, здесь также создается экземплярный метод безо всякого класса, т.к. он сам принадлежит этому классу и прекрасно видит ссылку this ). А еще замечательный случай, когда сразу замыкается и поле, и переменная одновременно (тут создается класс с двуми полями — одно под this, чтобы иметь доступ к полю, другое — под переменную).
то есть добавить перед return action строки
int xxx = count;
xxx++;
?
Ну и также в статье не описано, для чего вообще были введены замыкания, так как непонятно, зачем захватывать переменные, если можно просто их передать в виде параметра в функцию в делегате.
Здравствуйте, help-me, Вы писали:
HM>Ну и также в статье не описано, для чего вообще были введены замыкания, так как непонятно, зачем захватывать переменные, если можно просто их передать в виде параметра в функцию в делегате.
Замыкания нужны для поддержки ФП. Попробуйте написать без замыкания:
public static IEnumerable<int> OnlyGreaterThan(this IEnumerable<int> seq, int value)
{
return seq.Where(x => x > value); // значение value попадает в замыкание
}
Здравствуйте, adontz, Вы писали:
A>Дедуктивный метод тут не уместен, поведение просто описано в стандарте, а его логичность или не логичность не может служить докаательством.
Кстати в C# 5 для foreach теперь будет работать "логично":
Отсюда: Eric Lippert — Closing over the loop variable considered harmful:
UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed. We return you now to our original article.
PS: И кстати, в статье было бы неплохо это отразить.
Здравствуйте, fddima, Вы писали:
A>>Дедуктивный метод тут не уместен, поведение просто описано в стандарте, а его логичность или не логичность не может служить докаательством. F> Кстати в C# 5 для foreach теперь будет работать "логично": F>Отсюда: Eric Lippert — Closing over the loop variable considered harmful: F>
UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed. We return you now to our original article.
F> PS: И кстати, в статье было бы неплохо это отразить.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, help-me, Вы писали:
HM>>Ну и также в статье не описано, для чего вообще были введены замыкания, так как непонятно, зачем захватывать переменные, если можно просто их передать в виде параметра в функцию в делегате.
H>Замыкания нужны для поддержки ФП. Попробуйте написать без замыкания: H>
H>public static IEnumerable<int> OnlyGreaterThan(this IEnumerable<int> seq, int value)
H>{
H> return seq.Where(x => x > value); // значение value попадает в замыкание
H>}
H>
А если бы функцию Where реализовали бы не как функцию, принимающую делегат на функцию x => return bool , а как функцию, принимающую делегат на функцию (x, params object[] values) => return bool ? Тогда бы мы могли передавать неограниченное кол-во параметров и вместо захвата требуемого кол-ва переменных, мы могли бы передать их как параметры. Например, код выше заменился бы таким кодом:
public static IEnumerable<int> OnlyGreaterThan(this IEnumerable<int> seq, int value)
{
return seq.Where((x, value) => x > Convert.ToInt32(value));
}
или
public static IEnumerable<int> OnlyGreaterThan(this IEnumerable<int> seq, int value)
{
return seq.Where((x, new values[1]{value}) => x > Convert.ToInt32(values[0]));
}
Или, второй вариант, раз уж Microsoft предпочла ограничить нас в кол-ве параметров, передаваемых в делегат для Where: можно написать свой метод-расширение для Where, сделав так, чтобы он принимал нужное нам кол-во параметров, и вызывать метод-расширение, а не тот метод, который в LINQ.
Вот пример этого метода:
public static IEnumerable<int> OnlyGreaterThan(this IEnumerable<int> seq, int value)
{
return seq.WhereGreaterThan((x, value) => x > value);
}
//в методе, описанном ниже, могут быть ошибки, так как писал я его от руки прямо в браузере, а не в VSpublic static IEnumerable<int> WhereGreaterThan(this IEnumerable<int> seq, delegate Function<int x, int value, return bool>)//объявление делегата описано лишь для примера без точного синтаксиса, так как я не знаю точный синтаксис
{
IEnumerable<int> list = new List();
foreach(var item in seq)//проходимся по всему переданному списку, то есть просто сами реализуем то, что реализовано в стандартном Where...
{
if(Function())// ...лишь с тем отличием, что вызываем свой делегат с нужной нам сигнатурой
list.Add(item);
}
return list;
}
Или, 3-й вариант, незнаю, насколько он реальный, но можно попробовать наследовать нужный класс и переопределить свою реализвацию Where. Да, все варианты получаются более громоздкими, зато все очевидно и нет никаких скрытых операций, выполняемых компилятором (и, вследствие этого, возможно, влияния на скорость выполнения). То есть, судя по всему, возможность УДАЛЕНИЯ замыканий из языка C# все-же существует без ограничения функционала?
Здравствуйте, help-me, Вы писали:
HM>Или, 3-й вариант, незнаю, насколько он реальный, но можно попробовать наследовать нужный класс и переопределить свою реализвацию Where. Да, все варианты получаются более громоздкими, зато все очевидно и нет никаких скрытых операций, выполняемых компилятором (и, вследствие этого, возможно, влияния на скорость выполнения). То есть, судя по всему, возможность УДАЛЕНИЯ замыканий из языка C# все-же существует без ограничения функционала?
Рекомендую применить данные логические выкладки и к другим элементам языка C#, потом освоить Brainfuck и тогда ты достигнешь нирваны.
Здравствуйте, help-me, Вы писали:
HM>А если бы функцию Where реализовали бы не как функцию, принимающую делегат на функцию x => return bool , а как функцию, принимающую делегат на функцию (x, params object[] values) => return bool ? Тогда бы мы могли передавать неограниченное кол-во параметров и вместо захвата требуемого кол-ва переменных, мы могли бы передать их как параметры.
Смысл? Если любая комбинация условий в результате дает true/false