Seit kurzem beschäftige ich mich mit funktionaler Programmierung, prominente Kandidaten hierfür sind beispielsweise LISP oder Haskell und seit einiger Zeit auch Microsofts F#. Aber auch in nicht-funktionalen Programmiersprachen sind funktionale Konzepte anzutreffen, so auch in C# 3.0. Eines dieser Konzepte, die sogenannten Closures, haben mir schon des öfteren Kopfzerbrechen bereitet, ich habe sie einfach nicht verstanden, obwohl es eigentlich nicht allzu komplex erscheint. Dieser Post soll nun ein Erklärungsversuch meinerseits sein und ist quasi die Essenz aller Beiträge, die ich zu diesem Thema gelesen habe
Schauen wir doch erstmal bei Wikipedia nach, was man dort unter einer Closure versteht:
“…In computer science, a closure is a first-class function with free variables that are bound in the lexical environment. Such a function is said to be “closed over” its free variables…”
Alles klar soweit? Was sind denn jetzt first-class functions? In C# ist es ja möglich, einen Funktionsblock genau wie andere Datentypen einer Variable zuzuweisen und diesen Block später über den Variablennamen aufzurufen, genau wie normale Methoden auch. Das kann man z.B. über eine anonyme Methode oder einen Lambda-Ausdruck bewerkstelligen. Hier mal ein triviales Beispiel mit Lambdaausdruck:

Einem generischen Delegaten vom Typ Func<int, int> weise ich einen Lambdaausdruck zu, d.h. implizit zeigt der Delegat auf eine anonyme Methode, welche einen Parameter vom Typ int bekommt, diesen Eingangswert dann mit 6 multipliziert und das Ergebnis wieder als int-Wert zurückliefert. Die Möglichkeit, Funktionsblöcke derart zu verarbeiten machen sie zu first-class functions.
Und was ist mit den freien Variablen?
Freie Variablen sind einfach solche, welche weder als Parameter noch als lokale Variablen angelegt wurden.

Die Variable faktor ist innerhalb des Lambdaausdrucks bekannt und wird auch mit dem korrekten Wert 6 belegt. So etwas nennt man dann eine freie Variable. Wenn wir nun endlich zur Closure überleiten, wird es noch interessanter:

Was passiert denn nun hier? Über die Methode GibFunktion() lasse ich mir einen Delegaten in Form eines Lambdaausdrucks zurückgeben, soweit kein Hexenwerk. Bevor jetzt mit Steinen geworfen wird bliebe noch zu erwähnen, daß faktor als lokale Variable von GibFunktion() mit in den Delegaten gegeben wird und ja dann die Methode verlassen wird. Beim Aufruf schließlich wird in der anonymen Methode zuerst der Inhalt von faktor um eins von 5 auf 6 erhöht und dann das erwartete Ergebnis, nämlich 42, zurückgegeben. Wie kann das sein? Beim Verlassen der Methode GibFunktion() wird der Speicherplatz von faktor nicht wie üblich abgeräumt, weil ja in der anonymen Methode auf ihn verwiesen wird. Der Kontext, in dem faktor erzeugt wurde, existiert nicht mehr, trotzdem ist die Variable nach wie vor erhalten und steht auch ohne ihren Kontext zur Verfügung, sie ist an die anonyme Methode gebunden.
