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überstimmung einer Variablen dieses Typs zugewiesen werden können, dann direkt (mit den entsprechenden Argumenten) aufgerufen oder als Argument selbst 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 static string ReverseString(string s) Methode, die genau dieselbe Parameterliste und den Rückgabetyp wie der definierte Delegattyp aufweist, 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<>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 methode, die sie kapselt, 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 sie 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 können nun unser Beispiel oben betrachten und ihn mithilfe des Func<> Delegaten anstelle eines benutzerdefinierten Typs neu schreiben. 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 nur auf die geraden Zahlen und druckt sie dann in der Konsole.

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 können, ist der Textkörper der Stellvertretung nur eine Gruppe von Ausdrücken, wie jede andere Stellvertretung. 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 i => i % 2 == 0der verwendete Lambda-Ausdruck 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