Freigeben über


Delegaten und Lambdas

Ein Delegat definiert einen Typ, der Verweise auf Methoden darstellt, die eine bestimmte Parameterliste und einen bestimmten Rückgabetyp aufweisen. Eine Methode (statisch oder Instanz), deren Parameterliste und Rückgabetyp übereinstimmen, kann einer Variablen dieses Typs zugewiesen werden. Sie kann dann direkt (mit den entsprechenden Argumenten) aufgerufen oder selbst als Argument an eine andere Methode übergeben und dann aufgerufen werden. Im folgenden Beispiel wird die Verwendung von Stellvertretungen veranschaulicht.

using System;
using System.Linq;

public class Program
{
    public delegate string Reverse(string s);

    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Reverse rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}
  • Die public delegate string Reverse(string s); Zeile erstellt einen Delegattyp einer Methode, die einen Zeichenfolgenparameter akzeptiert, und gibt dann einen Zeichenfolgenparameter zurück.
  • Die Methode static string ReverseString(string s), die über die gleiche Parameterliste und den gleichen Rückgabetyp wie der definierte Delegattyp verfügt, implementiert den Delegaten.
  • Die Reverse rev = ReverseString; Zeile zeigt, dass Sie einer Variablen des entsprechenden Delegattyps eine Methode zuweisen können.
  • In der Console.WriteLine(rev("a string")); Zeile wird veranschaulicht, wie Sie eine Variable eines Delegatentyps verwenden, um den Delegaten aufzurufen.

Um den Entwicklungsprozess zu optimieren, enthält .NET eine Reihe von Delegattypen, die Programmierer wiederverwenden können und keine neuen Typen erstellen müssen. Diese Typen sind Func<>, Action<> und Predicate<>, und sie können verwendet werden, ohne neue Delegattypen definieren zu müssen. Es gibt einige Unterschiede zwischen den drei Typen, die mit der Art und Weise zu tun haben, wie sie verwendet werden sollen:

  • Action<> wird verwendet, wenn eine Aktion mithilfe der Argumente des Delegaten ausgeführt werden muss. Die von dem Typ gekapselte Methode gibt keinen Wert zurück.
  • Func<> wird in der Regel verwendet, wenn Sie eine Transformation zur Hand haben, d. h., Sie müssen die Argumente des Delegaten in ein anderes Ergebnis umwandeln. Projektionen sind ein gutes Beispiel. Die Methode, die es kapselt, gibt einen angegebenen Wert zurück.
  • Predicate<> wird verwendet, wenn Sie ermitteln müssen, ob das Argument die Bedingung des Delegaten erfüllt. Sie kann auch als eine Func<T, bool>geschrieben werden, was bedeutet, dass die Methode einen booleschen Wert zurückgibt.

Wir schreiben jetzt das obige Beispiel neu, indem wir den Func<>-Delegaten anstelle eines benutzerdefinierten Typs verwenden. Das Programm wird weiterhin genauso ausgeführt.

using System;
using System.Linq;

public class Program
{
    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Func<string, string> rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}

Für dieses einfache Beispiel scheint eine außerhalb der Main Methode definierte Methode etwas überflüssig zu sein. .NET Framework 2.0 hat das Konzept anonymer Stellvertretungen eingeführt, mit dem Sie "Inline"-Delegaten erstellen können, ohne zusätzlichen Typ oder eine zusätzliche Methode angeben zu müssen.

Im folgenden Beispiel filtert ein anonymer Delegat eine Liste nach geraden Zahlen und gibt sie dann auf der Konsole aus.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(
          delegate (int no)
          {
              return (no % 2 == 0);
          }
        );

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

Wie Sie sehen, besteht der Haupttext des Delegaten einfach nur aus einer Reihe von Ausdrücken, wie bei jedem anderen Delegaten auch. Anstatt aber eine separate Definition zu sein, haben wir sie ad hoc in unserem Aufruf der List<T>.FindAll Methode eingeführt.

Aber auch bei diesem Ansatz gibt es noch viel Code, den wir wegwerfen können. Hier kommen Lambda-Ausdrücke ins Spiel. Lambda-Ausdrücke oder nur "Lambdas" wurden kurz gesagt in C# 3.0 als einer der wichtigsten Bausteine von Language Integrated Query (LINQ) eingeführt. Sie sind nur eine komfortablere Syntax für die Verwendung von Stellvertretungen. Sie deklarieren eine Parameterliste und einen Methodentext, verfügen jedoch nicht über eine formale Identität, es sei denn, sie werden einem Delegaten zugewiesen. Im Gegensatz zu Delegaten können sie direkt als rechte Seite der Ereignisregistrierung oder in verschiedenen LINQ-Klauseln und -Methoden zugewiesen werden.

Da ein Lambda-Ausdruck nur eine andere Möglichkeit zum Angeben eines Delegaten ist, sollten wir in der Lage sein, das obige Beispiel neu zu schreiben, um einen Lambda-Ausdruck anstelle eines anonymen Delegaten zu verwenden.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(i => i % 2 == 0);

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

Im vorherigen Beispiel wird der Lambda-Ausdruck i => i % 2 == 0 verwendet. Auch hier handelt es sich nur um eine bequeme Syntax für die Verwendung von Delegaten. Was unter den Deckeln geschieht, ist ähnlich wie bei der anonymen Stellvertretung.

Lambdas sind wiederum nur Stellvertretungen, was bedeutet, dass sie als Ereignishandler ohne Probleme verwendet werden können, wie der folgende Codeausschnitt veranschaulicht.

public MainWindow()
{
    InitializeComponent();

    Loaded += (o, e) =>
    {
        this.Title = "Loaded";
    };
}

Der += Operator in diesem Kontext wird verwendet, um ein Ereignis zu abonnieren. Weitere Informationen finden Sie unter Abonnieren und Kündigen von Ereignissen.

Weiteres Lesen und Ressourcen