Mittwoch, 3. März 2010

F# und der Umgang mit out Parametern

Jede Funktion bzw. Methode kann zunächstmal nur immer eine Informationen zurückgegeben. Dies wird durch den Rückgabewert oder Rückgabeparameter ausgedrückt. Was macht man aber, wenn man in einer Methode mehrere Informationen zurückgeben möchte, die evtl. sogar optional sein sollen? Eine Möglichkeit ist natürlich einen komplexen Datentyp zu definieren und diesen als Rückgabetyp der Methode zu benennen. Aus Bequemlichkeit macht man das aber eher nicht.

Ein pragmatischster Ansatz ist es, für die zusätzlichen Rückgabewerte jeweils out Parameter zu definieren. Die out Parameter sind in der Regel primitive Datentypen wie string oder int.

Im .NET Framework gibt es einige Methoden die einen out Parameter haben. Eine oft verwendete Klasse ist z.B. das Dictionary.

Hier ein Beispiel:

var dict = new
Dictionary<string, int>();

dict.Add("A", 1);

dict.Add("B", 2);


 

Jetzt versuchen wir Werte aus dem Dictionary zu ziehen. Da nicht garantiert werden kann, dass der gesuchte Wert auch in dem Dictionary ist, verwenden wir die Methode TryGetValue. Da diese nun einen out Parameter definiert, müssen wir bevor wir diese Methode aufrufen können eine Variable mit dem entsprechenden out Parameter Typ definieren und die Methode mit der Variablen aufrufen.Der Rückgabewert von TryGetValue ist ein bool, so dass man erkennen kann, ob der Wert im Dictionary vorhanden ist. Nur wenn der Wert vorhanden ist, hat auch die Variable einen Wert. Aber Vorsicht! Da int ein Werttyp, ist hat er immer einen Defaultwert, nämlich 0. Zur Sicherheit sollte man daher der out Variablen einen anderen Wert als den Defaultwert zuweisen, bei int z.B: -1 statt o.


 

int value = -1;

var success = dict.TryGetValue("A", value);

if (success)

{


Console.WriteLine(value);

}

Soweit dürfte das jedem klar sein und sicherlich schon oft gemacht worden. Für mich ist die o.g. Methode, genauso wie TryParse, ein legitimer Grund out Paramter zu verwenden. Alle anderen Gründe sind eher zweitrangig da hier Bequemlichkeit im Vordergrund steht. Man will halt keinen komplexen Datentypen nur für einen Rückgabeparameter erstellen.

Aber es geht auch anders: mit F#!

F# to the rescue

Da F# eine ganz normale .NET Sprache ist, kann man natürlich auch die Klassen des Frameworks aufrufen. Z.B. auch die Methoden die out Parameter definieren, wie z.B. das Dictionary. Wie geht F# damit um?

In F# können wir ganz normal ein Dictionary erzeugen.

open System.Collections.Generic

let dict = new Dictionary<string,int>()

Nun fügen wir Werte zum Dictionary hinzu

dict.Add("A",1)

dict.Add("B",2)

Schließlich wollen wir die Werte auch wieder auslesen.

let result = dict.TryGetValue("A")

Nun von welchem Typ ist jetzt result? Und warum muss man keinen out Parameter übergeben?

F# Interactive gibt die Anwort.

val result : bool * int = (true, 1)

Result ist vom Typ bool * int
, was in F# bedeutet dass dies ein Tupel ist. In .NET 4.0 kann man dies als Tuple<bool,int>
beschreiben.

Nun können wir diesen Tupel mit den Funktionen fst und snd zerlegen.

let success = fst result

val success : bool = true


 

let value = snd result

val value : int = 1

F# wäre nicht F# wenn man es nicht noch einfacher ausdrücken könnte. Man kann nämlich den Tuple direkt in seine Bestandteile zerlegen.

let (success,value) = dict.TryGetValue("A")

val value : int = 1

val success : bool = true

let (success,value) = dict.TryGetValue("C")

val value : int = 0

val success : bool = false

Fazit

Für meine Begriffe ist dies ein gutes Bespiel wie ausdrucksstark F# ist. In der Vergangeneheit sind viele Fehler im Programmiersprachendesign gemacht worden. Allem voran C++, aber leider auch bei C#. Tuples sind ein hervoragendes Mittel, um out Parameter zu vermeiden. Da Tuple in .NET 4.0 ein "first Class" Datentyp ist, gibt es keinen Grund mehr diesen nicht auch in C# als Alternative zu out Parametern zu verweden. Letztendlich macht es aber mit F# mehr Spaß!

Over und out!