Action und Converter: Generic Delegates


Thomas Wölfer
Thomas Wölfer

26. April 2008


Sie stammen nicht aus dem "neuen" Visual Studio 2008 Paket, sondern es gibt sie schon seit .Net 2.0 und dem Visual Studio 2005: Die beiden Generic Delegates "Action" und "Converter". Und obwohl sie nun schon einige Jahre auf dem Buckel haben, habe ich es bis zu dieser Woche geschafft, die beiden vollkommen zu übersehen. Nachdem ich annehme das ich da nicht der einzige bin, ist das Grund genug, beide kurz unter die Lupe zu nehmen.

Das Action Generic Delegate

Die Dokumentation sagt dazu lapidar "Encapsulates a method that takes a single parameter and does not return a value.". Das ist schön. Die Frage ist aber: Wozu soll das gut sein?

Ein ganz typischer Fall ist der, das man eine Sammlung von Objekten hat, und für jedes dieser Objekte einen Vorgang durchführen möchte. Gängigerweise tut man dann folgendes:

foreach( TypeOfObject obj in sammlung)
{
   DoSomethingWithObject( obj );
}

Weil man das so oft braucht, hat List<T> ein eigenes ForEach. Hat man beispielsweise eine List<string>, kann man auch folgendes Konstrukt verwenden:

list.ForEach( DoSomethingWithString );  // (DoSomethingWithString ist eine Methode die einen string als Parameter bekommt.

List<T> verwendet als Parameter den Action-Delegate. Das ganze geht dann auch anonym, also ohne explizit eine DoSomethingWithString( string s) Methode zu implementieren:

list.ForEach( delegate( string s)
{
   Console.WriteLine( s );
});

Nun muss man sich ein etwas umfangreicheres Konstrukt vorstellen: Man hat eine Sammlung an Objekten und muss für alle Objekte mehrere Aufgabe durchführen: Die Aufgaben sind im wesentlichen gleich, unterscheiden sich aber durch einen bestimmten Vorgang. Auch hier hilft das Action-Delegate.

void foo1()
{
   Action<Control> showNameDelegate = delegate(Control c) { MessageBox.Show(c.Name); };
   DoWork(showNameDelegate,
this.Controls);
}

void foo2()
{
   Action<Control> showSizeDelegate = delegate(Control c) { MessageBox.Show(c.Size.ToString()); };
   DoWork(showSizeDelegate,
this.Controls);
}

private void DoWork(Action<Control> action, System.Windows.Forms.Control.ControlCollection controls)
{
   foreach (Control control in controls)
   {
      // hier wird "allgemeingltige" arbeit mit dem
      // control durchgefhrt.

      action(control);

      // hier wird "allgemeingltige" arbeit mit dem
     // control durchgefhrt.
  
}
}

Man kann dadurch also die "allgemeingültige" Arbeit mit einer Methode versorgen, die die unterschiedlichen Aspekte übernimmt.

Das Converter Generic Delegate

Hier geht es um folgendes: Das Delegate bekommt ein Objekt von einem Typ und liefert eines von einem anderen Typ. Angenommen man hat eine Methode zum anzeigen einer Form, die, bevor sie die Form anzeigt, viele aufwendige Dinge mit der Form tut. (Im Beispiel ist das nur das Verändern des FormBorderStyle, aber die Sache kann ja auch aufwendiger werden.)

Nun will man aber im einen Fall die Form Modal anzeigen, im anderen nicht. Wird Sie modal angezeigt, muss das DialogResult geliefert werden: Die Form wird also effektiv in ein DialogResult "umgewandelt". Im nicht-modalen Fall passiert das auch, nur ist das DialogResult dann "None".

Mit dem Converter Delegate ist das kein Problem:

        private void button1_Click_1(object sender, EventArgs e)
        {
            Converter<Form, DialogResult> showFormDelegate = delegate(Form f) { return f.ShowDialog(); };

            DoShowForm(showFormDelegate, new Form1());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Converter<Form, DialogResult> showFormDelegate = delegate(Form f) { f.Show(); return DialogResult.None; };

            DoShowForm(showFormDelegate, new Form1());
        }


        private DialogResult DoShowForm(Converter<Form, DialogResult> showFormDelegate, Form f)
        {
            f.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            // ... sowie andere taetigkeiten

            return showFormDelegate(f);
        }