Мои 2 pennies:-
collection.Count(v => (v.PropertyToUpdate = newValue) == null);
Замыкания не дают вам никаких дополнительных возможностей.
Все, что вы можете достичь с их помощью, вы можете достичь и без них.
Но они очень удобны для того, чтобы сделать код более ясным и читаемым. . И, как мы все знаем, чистый читаемый короткий код - это код, который легче отлаживать и который содержит меньше ошибок.
Позвольте мне привести краткий пример возможного использования Java:
button.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
System.out.println("Pressed");
}
});
Будет заменен (если бы Java имел закрытие) на:
button.addActionListener( { System.out.println("Pressed"); } );
Вы можете рассматривать это как обобщение класса.
Ваш класс хранит некоторое состояние. У него есть некоторые переменные-члены, которые могут использовать его методы.
Замыкание - это просто более удобный способ предоставить функции доступ к локальному состоянию.
Вместо того, чтобы создавать класс, который знает о локальной переменной, которую вы хотите функцию, вы можете просто определить функцию на месте, и она может неявно обращаться к каждой переменной, которая в настоящее время видима.
Когда вы определяете метод члена на традиционном языке ООП, его закрытие "все члены, видимые в этот класс ».
Языки с« правильной »поддержкой замыкания просто обобщают это, поэтому закрытие функции - это« все переменные, видимые здесь ». Если « здесь » - это класс, то у вас есть традиционный метод класса.
Если « здесь » находится внутри другой функции, то у вас есть то, что функциональные программисты считают закрытием. Теперь ваша функция может получить доступ ко всему, что было видно в родительской функции.
Так что это просто обобщение, снимающее глупое ограничение, что «функции могут быть определены только внутри классов», но сохраняя идею, что «функции могут видеть любые переменные. видны в том месте, где они объявлены ».
IMHO, это сводится к возможности захвата блоков кода и их контекста, чтобы на них ссылаться в какой-то момент позже и выполнять, когда / если / по мере необходимости .
Они могут показаться незначительными, и замыкания определенно не то, что вам нужно для повседневной работы, но они могут сделать код кода намного проще и понятнее для написания. /manage.
[edit - пример кода на основе комментария выше]
Java:
List<Integer> numbers = ...;
List<Integer> positives = new LinkedList<Integer>();
for (Integer number : integers) {
if (number >= 0) {
positives.add(number);
}
}
Scala (пропущены некоторые другие тонкости, такие как вывод типов и подстановочные знаки, поэтому мы сравниваем только эффект закрытия):
val numbers:List[Int] = ...
val positives:List[Int] = numbers.filter(i:Int => i >= 0)
Вот несколько интересных статей:
Замыкания очень хорошо вписываются в объектно-ориентированный мир.
В качестве примера рассмотрим C # 3.0: он имеет замыкания и многие другие функциональные аспекты, но по-прежнему является очень объектно-ориентированным языком.
1266] По моему опыту, функциональные аспекты C # имеют тенденцию оставаться в рамках реализации членов класса, а не столько как часть общедоступного API, который в конечном итоге раскрывают мои объекты.
Таким образом, использование замыканий, как правило, является деталями реализации в объектно-ориентированном коде.
Я использую их постоянно, как показывает этот фрагмент кода из одного из наших модульных тестов (против Moq ):
var typeName = configuration.GetType().AssemblyQualifiedName;
var activationServiceMock = new Mock<ActivationService>();
activationServiceMock.Setup(s => s.CreateInstance<SerializableConfigurationSection>(typeName)).Returns(configuration).Verifiable();
Было бы довольно сложно указать входное значение (typeName) как часть ожидания Mock, если бы не функция закрытия C #.
Возможно, в мире компилируемого программирования преимущества замыканий менее заметны. В JavaScript закрытие невероятно мощно. Это происходит по двум причинам:
1) JavaScript - это интерпретируемый язык, поэтому эффективность инструкций и сохранение пространства имен невероятно важны для более быстрого и более отзывчивого выполнения из больших программ или программ, которые оценивают большие объемы ввода.
2) JavaScript это лямбда-язык. Это означает, что в функциях JavaScript являются объектами первого класса, которые определяют область действия, а область видимости родительской функции доступна для дочерних объектов. Это важно, поскольку переменная может быть объявлена в родительской функции, использоваться в дочерней функции и сохранять значение даже после возврата из дочерней функции.
Для меня самое большое преимущество замыканий - это когда вы пишете код, который запускает задачу, оставляя задачу на усмотрение run и указывает, что должно произойти, когда задача будет выполнена. Обычно код, который выполняется в конце задачи, требует доступа к данным, доступным в начале, и закрытие упрощает это.
Например, обычное использование в JavaScript - запуск HTTP-запроса. Тот, кто его запускает, вероятно, хочет контролировать, что происходит, когда приходит ответ. Итак, вы сделаете что-то вроде этого:
function sendRequest() {
var requestID = "123";
Ext.Ajax.request({
url:'/myUrl'
success: function(response) {
alert("Request " + requestID + " returned");
}
});
}
Из-за закрытий JavaScript переменная "requestID" захватывается внутри функции успеха. Это показывает, как вы можете написать функции запроса и ответа в одном месте и совместно использовать переменные между ними. Без закрытий вы
Жалко, люди больше не изучают Smalltalk в edu; там замыкания используются для структур управления, обратных вызовов, перечисления коллекций, обработки исключений и многого другого. В качестве небольшого приятного примера, вот поток обработчика действий рабочей очереди (в Smalltalk):
|actionQueue|
actionQueue := SharedQueue new.
[
[
|a|
a := actionQueue next.
a value.
] loop
] fork.
actionQueue add: [ Stdout show: 1000 factorial ].
и, для тех, кто не умеет читать Smalltalk, то же самое в синтаксисе JavaScript:
var actionQueue;
actionQueue = new SharedQueue;
function () {
for (;;) {
var a;
a = actionQueue.next();
a();
};
}.fork();
actionQueue.add( function () { Stdout.show( 1000.factorial()); });
(ну, как вы видите: синтаксис помогает в чтение кода)
Изменить: обратите внимание, как на actionQueue ссылаются изнутри блоков, что работает даже для разветвленного блока потока. Вот почему укупорочные средства так просты в использовании.