C# im Fokus

Delegates und anonyme Methoden
Kommentare

Die .NET-Plattform hat sich in den letzten Jahren stetig weiter entwickelt. Gestartet mit der .NET-Version 1.0, ist nun die Version 4.0 verfügbar. Hinzugekommen sind dabei nicht nur neue und erweiterte Bibliotheken, sondern auch der Sprachumfang ist erweitert worden. In dieser Ausgabe des dot.NET Magazins startet die neue C#-Kolumne, die jeweils elementare Sprachfeatures aufgreift und praktisch erläutert.

Mit jeder neuen Version des .NET Frameworks werden neue Bibliotheken sowie Sprachfeatures eingeführt. Neue Bibliotheken werden in der Regel von einer Vielzahl von Entwicklern angenommen und verwendet. Bei neuen Sprachfeatures sieht das meist anders aus. Oft sind diese zwar namentlich bekannt, werden aber nur vereinzelt verwendet. Der Einsatz neuer Sprachfeatures kann aber an vielen Stellen den Code lesbarer und effizienter gestalten. Die Kolumne „Im Fokus C#“ greift jeweils bestimmte Sprachmerkmale auf und stellt diese praktisch vor. Dabei werden nicht nur neue Merkmale erläutert, sondern auch Eigenschaften, die seit der ersten .NET-Version verfügbar sind. Die Kolumne startet mit Delegates und stellt die Nutzung anonymer Methoden vor.

Arbeitsweise von Delegates

Wörtlich übersetzt bedeutet Delegate „Sellvertreter“, wobei im deutschen Sprachgebrauch eher der Begriff „Funktionszeiger“ (Delegaten) üblich ist. Die beiden Begriffe beschreiben die Funktion eines Delegaten aber schon recht gut. Ein Delegate ist in der Lage, die Aufrufadresse einer Methode zu speichern. Somit „zeigt“ der Delegate auf die Funktion (Begriff: „Funktionszeiger“). Wird der Delegate aufgerufen, ruft dieser „stellvertretend“ die Methode auf, auf die der Delegate verweist (Begriff: „Stellvertreter“). Listing 1 zeigt die einfache Verwendung eines Delegates in C#.

delegate void SimpleDelegate();
public static void DelegateSimple()
{
  SimpleDelegate myDel = new SimpleDelegate(CallMe);
  myDel();
}
public static void CallMe()
{ Console.WriteLine("Delegate !"); }  

Listing 1

Zunächst wird die Signatur der Methode für den Funktionszeiger durch die Deklaration des Delegaten festgelegt. Im Beispiel kann der definierte Delegate SimpleDelegate auf jede Methode verweisen, die keinen Rückgabewert sowie keine Parameter definiert. Die Bedingung erfüllt die Methode CallMe, die lediglich eine Ausgabe auf der Konsole ausgibt. Die Zuweisung dieser Methode zum Delegate erfolgt während der Neuanlage des Delegates über den Konstruktur. Wurde diese Verbindung hergestellt, kann über die angelegte Delegate-Variable die Methode aufgerufen werden. Auch die Verwendung von Parametern und Rückgabewerten ist möglich, hierbei ändert sich lediglich die Signaturdefinition des Delegaten. Ein entsprechendes Beispiel ist in Listing 2 zu sehen. Weiterführende Informationen zum Delegate-Typ sind unter [1] zu finden.

delegate int Add(int a, int b);
public static void DelegateParameter()
{
  Add myDel = new Add(Calculate);
  int sum = myDel(5, 10);
}
public static int Calculate(int x, int y)
{  return x + y; }  

Listing 2

Verwendung von Delegates

Innerhalb des .NET Frameworks existieren zahlreiche Methoden, die ein Delegate als Parametertyp erwarten. Das Beispiel in Listing 3 verdeutlicht die Verwendung anhand der Methode FindAll der generischen List-Klasse.

static List arr = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
                                       13, 14, 15, 16, 17, 18, 19, 20 };
public static void Delegates()
{
  List result = arr.FindAll(new Predicate(IsMatch));
  foreach (var item in result)
    Console.WriteLine(item);
}
public static bool IsMatch(int figure)
{ return (figure % 2)==0; }  

Listing 3

Ziel der Implementierung ist es, die geraden Zahlen aus der int-Liste zu ermitteln. Die Methode FindAll erwartet dazu einen Funktionszeiger folgender Struktur: public delegate bool Predicate (T obj). Um im weiteren Verlauf den Vorteil anonymer Methoden erläutern zu können, wird hier zunächst eine benannte Methode definiert, auf die der Funktionszeiger verweisen soll. Im Beispiel wurde dafür die Methode IsMatch angelegt. Während der Erzeugung des Predicate Delegate wird wieder die Verknüpfung zwischen Methode und Funktionszeiger hergestellt. Die Methode FindAll ruft anschließend über den Funktionszeiger die Methode IsMatch auf, um die passenden Zahlen zu ermitteln.

Delegate Inference

Der C# Compiler besitzt seit der .NET-Version 2.0 noch eine weitere interessante Eigenschaft, die auch die Verwendung von Delegates vereinfacht. Dabei handelt es sich um das so genannte Delegate Inference. Diese Eigenschaft ermöglicht es dem Compiler, Rückschlüsse auf den benötigten Typ zu ziehen. Somit entfällt die explizite Instanziierung eines bestimmten Delegates. Listing 4 zeigt, wie sich Delegate Inference auf die Codierung auswirkt. Wie erkennbar ist, entfällt die explizite Erzeugung des Predicate-Objekts und es wird lediglich der Name der Methode angegeben.

static List arr = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
                                       13, 14, 15, 16, 17, 18, 19, 20};
    
public static void Test()
{
  List result = arr.FindAll(IsMatch);
  foreach (var item in result)
    Console.WriteLine(item);
}
public static bool IsMatch(int figure)
{ return (figure % 2)==0; }  

Listing 4

Während der Übersetzungszeit (Compile Time) ermittelt der Compiler als Erstes den nötigen Delegate-Typen. Danach prüft der Compiler, ob die angegebene Methode existiert und die Methodensignatur dem Delegate-Typen entspricht. Wenn beide Bedingungen zutreffen, erzeugt der Compiler intern das nötige Delegate-Objekt und weist diesem die angegebene Methode zu.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -