Mit oder ohne Namen – Funktionen und Methoden

Die Funktion von Funktionen
Kommentare

Alle .NET-Programme bestehen letztlich aus Funktionen. Diese mögen in Klassen angeordnet sein, in Modulen oder Namespaces und sie kommen in Varianten mit und ohne Namen daher. Natürlich gibt es eine scheinbar unendlich große Zahl von Syntaxvarianten. Hier werden die aktuellen Möglichkeiten zusammengefasst.

Bereits seit .NET 1 und C# 1.0 ist es möglich, sehr flexibel mit Funktionen und Referenzen auf Funktionen zu arbeiten. Der Ausdruck „Funktionen“ wird hier bewusst gewählt, um von „Methoden“ zu unterscheiden – während sich letztere immer in Klassen befinden müssen, sind erstere aus Sicht des Programmierers flexibler, da sie auch innerhalb anderer Funktionen (oder Methoden) erzeugt, anderswohin übergeben oder als Parameter entgegengenommen werden können. In vergangenen C#-Corner-Artikeln war schon oft von Lambda-Expressions die Rede, es schien jedoch an der Zeit, einmal eine Bestandsaufname zu machen, mit den verschiedenen Syntaxvarianten, die zum aktuellen Zeitpunkt (C# 3.0) zur Verfügung stehen. .NET- und C#-Programmierer sind natürlich bestens vertraut mit der Erstellung von Methoden in Klassen, denn das ist der wichtigste Ansatz, der in der objektorientierten Programmierung verwendet wird.

Um Methoden in Variablen zu speichern (oder anders gesagt eine Referenz auf eine Methode in einer Variablen abzulegen), werden seit .NET 1 Delegate-Typen unterstützt. Eine Delegate-Definition kann innerhalb oder außerhalb von Klassen stehen und verwendet Sichtbarkeitsdefinitionen wie andere Elemente in C# auch. Hier ein einfaches Beispiel: delegate void OutputDelegate(string value);. Diese Anweisung definiert den Delegate-Typ OutputDelegate (lassen Sie sich nicht davon verwirren, dass der Name des Elements in der Mitte erscheint). Wichtiger als der Name sind die Parameterliste (hier ein String-Parameter) und der Rückgabewert des Typs (hier void). Funktionen können mit einem Delegate-Typ kompatibel sein oder auch nicht und der Compiler richtet sich hier primär nach den Parametern und dem Rückgabetyp. Diese Funktion ist kompatibel mit dem Delegate-Typ OutputDelegate (obwohl der Parameter einen anderen Namen hat – darauf kommt es nicht an):

void Output(string aValue) {
}  

Delegates können auch generisch sein. Hier ein etwas flexibleres Beispiel für OutputDelegate: delegate void OutputDelegate(T value);. Die Funktion Output oben ist nach wie vor kompatibel mit diesem Delegate, und die folgende Funktion ebenso:

void Output(int value) {
}  

Delegate-Typen sind die Datentypen, in denen Referenzen auf Funktionen gespeichert werden. Somit verwenden Sie diese Typen, wenn Funktionen andere Funktionen als Parameter entgegennehmen. In diesem Beispiel gibt es eine generische Output-Funktion, die mithilfe eines Delegates beliebige Datentypen verarbeiten kann:

void Output(OutputDelegate outputFunction, T value) {
  outputFunction(value);
}  

Das Beispiel zeigt auch, wie Funktionen, die in Delegate-Typen abgelegt worden sind, aufgerufen werden: ebenso wie alle Funktionen, mit Übergabe der notwendigen Parameter über die Variable, die die Referenz enthält. Ein Zusatz zum System der Delegate-Typen sind Events und Multicast Delegates, die Benachrichtigungen an mehrere Empfänger gleichzeitig ermöglichen. Das ist eine wichtige Funktion, in der die Features, die in diesem Artikel besprochen werden, eingesetzt werden. Allerdings gehen die Details hier etwas zu weit, weshalb der Hinweis an dieser Stelle ausreichen soll.

Seit .NET 2 und C# 2.0 gibt es anonyme Methoden. Der Name „Methoden“ ist etwas verwirrend, da diese nicht wirklich in Klassen angelegt werden – zumindest nicht von der Perspektive des Programmierers. Technisch betrachtet, legt der Compiler tatsächlich eine Methode in der Klasse an, wenn dieses Feature verwendet wird, und das lag vermutlich der Namensgebung zugrunde. Anonyme Methoden verwenden das Schlüsselwort „delegate“, weshalb sie auch manchmal als Delegates bezeichnet werden. Leider werden auch die oben erklärten Delegate-Typen oft kurz Delegates genannt, sodass hier viel Potential für Missverständnisse besteht. In diesem Beispiel wird eine anonyme Methode erzeugt, die mit dem generischen OutputDelegate kompatibel ist:

OutputDelegate myOutput = delegate(string value) {
  Console.WriteLine(value);
};
Output(myOutput, "Using C# 2.0 anonymous method");  

Seit C# 3.0 ist es möglich, anonyme Methoden auch als Lambda-Ausdrücke zu schreiben. Damit sieht das obige Beispiel so aus: OutputDelegate myOutput = value => Console.WriteLine(value). Die Funktionen, die in beiden Beispielen entstehen, sind identisch, aber die Syntax ist offensichtlich unterschiedlich. In Lambda-Expressions kann häufig Typherleitung verwendet werden, sodass der Typ des Parameters value oben nicht explizit angegeben werden muss. Ebenso ist es nicht notwendig, in einfachen Anwendungsfällen explizit ein „return“ zu schreiben – der Ausdruck auf der rechten Seite des „=>“ (gesprochen „goes to“) wird automatisch als Rückgabewert verwendet. Im Beispiel oben gibt es noch nicht einen Rückgabewert, was ebenfalls akzeptiert wird.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -