Delegaten und LambdasDelegates and lambdas

Delegaten definieren einen Typ, der eine bestimmte Methodensignatur angibt.Delegates define a type, which specify a particular method signature. Eine Methode (statische Methode oder Instanzmethode), die dieser Signatur entspricht, kann einer Variablen dieses Typs zugewiesen werden. Anschließend kann sie (mit den entsprechenden Argumenten) direkt aufgerufen oder selbst als Argument an eine andere Methode übergeben und dann aufgerufen werden.A method (static or instance) that satisfies this signature can be assigned to a variable of that type, then called directly (with the appropriate arguments) or passed as an argument itself to another method and then called. Das folgende Beispiel veranschaulicht die Verwendung von Delegaten.The following example demonstrates delegate use.

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"));
  }
}
  • In Zeile 4 erstellen wir einen Delegattyp mit einer bestimmten Signatur – in diesem Fall eine Methode, die einen Zeichenfolgenparameter akzeptiert und dann einen Zeichenfolgenparameter zurückgibt.On line 4 we create a delegate type of a certain signature, in this case a method that takes a string parameter and then returns a string parameter.
  • In Zeile 6 definieren wir die Implementierung des Delegaten, indem wir eine Methode bereitstellen, die exakt die gleiche Signatur aufweist.On line 6, we define the implementation of the delegate by providing a method that has the exact same signature.
  • In Zeile 13 wird die Methode einem Typ zugewiesen, der dem Reverse-Delegaten entspricht.On line 13, the method is assigned to a type that conforms to the Reverse delegate.
  • In Zeile 15 rufen wir schließlich den Delegaten auf, indem wir eine Zeichenfolge übergeben, die umgekehrt werden soll.Finally, on line 15 we invoke the delegate passing a string to be reversed.

Um den Entwicklungsprozess zu optimieren, enthält .NET eine Reihe von Delegattypen, die Programmierer verwenden können, damit sie keine neuen Typen erstellen müssen.In order to streamline the development process, .NET includes a set of delegate types that programmers can reuse and not have to create new types. Dies sind Func<>, Action<> und Predicate<>. Sie können an verschiedenen Stellen in den .NET-APIs verwendet werden, ohne dass neue Delegattypen definiert werden müssen.These are Func<>, Action<> and Predicate<>, and they can be used in various places throughout the .NET APIs without the need to define new delegate types. Natürlich gibt es Unterschiede zwischen den drei Typen, wie Sie in ihren Signaturen erkennen können. Hierbei handelt es sich meist um Unterschiede in der Art und Weise der Verwendung dieser Typen:Of course, there are some differences between the three as you will see in their signatures which mostly have to do with the way they were meant to be used:

  • Action<> wird verwendet, wenn eine Aktion mithilfe der Argumente des Delegaten ausgeführt werden muss.Action<> is used when there is a need to perform an action using the arguments of the delegate.
  • Func<> wird üblicherweise verwendet, wenn eine Transformation ausgeführt werden muss, Sie also die Argumente des Delegaten in ein anderes Ergebnis transformieren müssen.Func<> is used usually when you have a transformation on hand, that is, you need to transform the arguments of the delegate into a different result. Projektionen sind ein gutes Beispiel hierfür.Projections are a prime example of this.
  • Predicate<> wird verwendet, wenn Sie ermitteln müssen, ob das Argument die Bedingung des Delegaten erfüllt.Predicate<> is used when you need to determine if the argument satisfies the condition of the delegate. Der Typ kann auch als Func<T, bool> geschrieben werden.It can also be written as a Func<T, bool>.

Wir schreiben jetzt das obige Beispiel neu, indem wir den Func<>-Delegaten anstelle eines benutzerdefinierten Typs verwenden.We can now take our example above and rewrite it using the Func<> delegate instead of a custom type. Das Programm wird weiterhin genau gleich ausgeführt.The program will continue running exactly the same.

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"));
  }
}

In diesem einfachen Beispiel erscheint es überflüssig, eine Methode außerhalb der Main()-Methode zu definieren.For this simple example, having a method defined outside of the Main() method seems a bit superfluous. Aus diesem Grund wurde in .NET Framework 2.0 das Konzept der anonymen Delegaten eingeführt.It is because of this that .NET Framework 2.0 introduced the concept of anonymous delegates. Damit können Sie „Inlinedelegaten“ erstellen, ohne einen weiteren Typ oder eine zusätzliche Methode angeben zu müssen.With their support you are able to create "inline" delegates without having to specify any additional type or method. Sie fügen einfach die Definition des Delegaten inline dort ein, wo Sie sie benötigen.You simply inline the definition of the delegate where you need it.

Als Beispiel verwenden wir unseren anonymen Delegaten, um eine Liste mit gerade Zahlen herauszufiltern und diese Zahlen in der Konsole auszugeben.For an example, we are going to switch it up and use our anonymous delegate to filter out a list of only even numbers and then print them to the console.

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);
    }
  }
}

Beachten Sie die markierten Zeilen.Notice the highlighted lines. Wie Sie sehen, besteht der Haupttext des Delegaten einfach nur aus einer Reihe von Ausdrücken, wie bei jedem anderen Delegaten auch.As you can see, the body of the delegate is just a set of expressions, as any other delegate. Anstatt aber eine separate Definition zu erstellen, haben wir ihn ad hoc in den Aufruf der FindAll()-Methode des List<T>-Typs eingeführt.But instead of it being a separate definition, we’ve introduced it ad hoc in our call to the FindAll() method of the List<T> type.

Selbst bei dieser Vorgehensweise bleibt immer noch zu viel Code übrig, den wir nicht benötigen.However, even with this approach, there is still much code that we can throw away. Hier kommen Lambdaausdrücke ins Spiel.This is where lambda expressions come into play.

Lambdaausdrücke (sogenannte Lambdas) wurden zum ersten Mal in C# 3.0 eingeführt und stellen einen der wichtigsten Bausteine von LINQ dar (Language Integrated Query).Lambda expressions, or just "lambdas" for short, were introduced first in C# 3.0, as one of the core building blocks of Language Integrated Query (LINQ). Sie sind einfach eine praktischere Syntax für die Verwendung von Delegaten.They are just a more convenient syntax for using delegates. Sie deklarieren eine Signatur und einen Methodentext, besitzen aber keine eigene formale Identität, sofern sie nicht einem Delegaten zugewiesen sind.They declare a signature and a method body, but don’t have an formal identity of their own, unless they are assigned to a delegate. Im Gegensatz zu Delegaten können sie direkt als linke Seite der Ereignisregistrierung oder in verschiedenen LINQ-Klauseln und -Methoden zugewiesen werden.Unlike delegates, they can be directly assigned as the left-hand side of event registration or in various Linq clauses and methods.

Da ein Lambdaausdruck einfach nur eine weitere Möglichkeit ist, einen Delegaten anzugeben, können wir das obige Beispiel neu schreiben und einen Lambdaausdruck anstelle eines anonymen Delegaten verwenden.Since a lambda expression is just another way of specifying a delegate, we should be able to rewrite the above sample to use a lambda expression instead of an anonymous delegate.

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);
    }
  }
}

In den markierten Zeilen sehen Sie, wie ein Lambdaausdruck aussieht.If you take a look at the highlighted lines, you can see how a lambda expression looks like. Das ist einfach eine sehr praktische Syntax für die Verwendung von Delegaten. Hinter den Kulissen passiert das Gleiche wie bei anonymen Delegaten.Again, it is just a very convenient syntax for using delegates, so what happens under the covers is similar to what happens with the anonymous delegate.

Noch einmal: Lambdas sind einfach Delegaten. Das bedeutet, dass sie problemlos als Ereignishandler verwendet werden können, wie im folgenden Codeausschnitt veranschaulicht.Again, lambdas are just delegates, which means that they can be used as an event handler without any problems, as the following code snippet illustrates.

public MainWindow()
{
    InitializeComponent();

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

Weitere Informationen und RessourcenFurther reading and resources