Archive

Archive for May, 2009

Fünf Freunde: Action, Predicate, Comparison, Converter und Lambda Ausdrücke

May 21st, 2009 Bernd 5 comments

Ich habe den Eindruck, dass die generischen Delegates aus dem .Net Framework vielerorts immer noch ein Schattendasein fristen. Mit ein paar einfachen Beispielen die zeigen, wofür man Action, Predicate und Co. verwenden kann, möchte ich Lust auf mehr machen.

Generische Delegates ersparen dem Entwickler die Arbeit eigene Delegates zu deklarieren und kommen häufig in Verbindung mit Containerklassen zur Anwendung.

Als Spielzeugklasse habe ich mir diesmal etwas ganz aussergewöhnliches einfallen lassen. Und zwar eine Klasse Person:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsCool { get; set; }

    public override string ToString()
    {
        return String.Format( "{0} is {1}.", Name, IsCool ? "cool" : "not cool" );
    }
}

Dazu erzeugen wir uns noch ein paar "Versuchspersonen":

List<Person> persons = new List<Person>();

persons.Add( new Person {Name = "Karin", Age = 39} );
persons.Add( new Person {Name = "Simon", Age = 23} );
persons.Add( new Person {Name = "Fritz", Age = 34} );
persons.Add( new Person {Name = "Georg", Age = 56} );
persons.Add( new Person {Name = "Susi", Age = 67} );
persons.Add( new Person {Name = "Toni", Age = 45} );

Action<T> Delegate

Der Action<T> Delegate kapselt eine Methode die einen Parameter übergeben bekommt und keinen Wert zurückgibt. Mit .Net 3.5 wurden noch zusätzliche Action Delegates eingeführt, die bis zu vier Parameter bekommen (Action<T1, T2>, Action<T1, T2, T3> und Action<T1, T2, T3, T4>).

Ein Action Delegate wird z.B. als Parameter für die ForEach Methode der Klasse List<T> benötigt. ForEach iteriert über die gesamte Liste und führt für jedes Element die übergebene Methode aus.

Action<T> delegate

Hier ein paar Beispiele:

Informationen über alle unsere Personen auf der Console ausgeben:

persons.ForEach(Console.WriteLine);

In Verbindung mit einem Lambdaausdruck werden alle Personen ganz schnell cool:

persons.ForEach(p => p.IsCool = true);

Predicate<T> Delegate

Der Predicate<T> Delegate kapselt eine Methode die einen Parameter (vom Typ T) übergeben bekommt und true oder false zurückgibt. Es steckt aber auch Semantik in dem Predicate Delegate: er definiert einen Satz von Kriterien und überprüft ob das übergebene Objekt diese Kriterien erfüllt. Das klingt erstmal ziemlich geschwollen, aber ein Beispiel macht das schnell klar.

Predicate<T> delegate

Um z.B. alle Personen die älter als 50 sind zu entfernen, schreiben wir:

persons.RemoveAll( p => p.Age > 50 );

Mit der Methode Exists kann überprüft werden, ob in der Liste Element enhalten sind, die eine bestimmte Bedingung erfüllen:

if (persons.Exists(p => p.Name == "Toni"))
{
    Console.WriteLine("A person with name Toni exists");
}

Comparison<T> Delegate

Der Comparison<T> Delegate kapselt eine Methode die zwei Objekte gleichen Typs vergleicht.

public delegate int Comparison<T>
(
	T x,
	T y
)

Als Rückgabewert dient ein Integer der anzeigt, ob die beiden Objekte gleich sind:

Rückgabewert Bedingung
Kleiner als 0 x ist kleiner als y
0 x und y sind gleich
Größer als 0 x ist größer als y

Um die Liste von Personen nach dem Alter aufsteigend zu sortieren, übergebe ich der Sort() Methode einen Comparison Delegate:

image

persons.Sort( (person1, person2) => person1.Age.CompareTo( person2.Age ) );

Converter<TInput, TOutput> Delegate

Der Converter<TInput, TOutput> Delegate repräsentiert eine Methode die ein Objekt vom Typ TInput als Parameter bekommt und ein Objekt vom Typ TOutput zurückgibt. Der Hauptanwendungsfall ist das Konvertieren von Elementen in einem Array oder einer Liste.

Die Liste von Personen kann z.B. einfach in eine Liste von Namen konvertiert werden:

List<string> names = persons.ConvertAll( p => p.Name );

Durch das Naming der Methode ConvertAll geht auch sehr schön hervor, dass es sich um eine Konvertierung handelt. Dadurch entsteht lesbarer, verständlicher Code.

 

Die generischen Delegates des.Net Frameworks, bieten eine elegante Möglichkeit mit den Containerklassen zu arbeiten. Durch die Verwendung von Lambda Ausdrücken führt das Finden, Sortieren usw. von Listen zur sehr schönem, lesbaren Code. Ich finde die generischen Delegates klasse!