Aus der Rubrik: Neues von der Basta heute meine zweite Session C# Phase 4 mit Christian Nagel
Seit C# 4.0 unterstützt unsere geliebte Entwicklungssprache, diese recht interessante Feature. In diesem Blog Post, möchte ich diese für C# Entwickler neue Möglichkeit, einmal etwas genauer betrachten.
Obwohl die optionalen Parameter ganz klar sehr nützlich sein können, so bergen Sie auch einige Gefahren die man beachten sollte , aber fangen wir erst einmal langsam an.
Optionale Parameter was ist das eigentlich?
Manche Entwickler unter euch, werden es schon z.B. aus der VB Zeit heraus kennen.
Ihr kennt das doch, Ihr schreibt eine Methode mit sagen wir mal 5 Parameter, jedoch Ihr braucht nicht immer alle Parameter. wie in diesem Beispiel
public string BuildSQL(string table) { return BuildSQL(table, "*"); }
public string BuildSQL(string table, string columns) { return BuildSQL(table, columns, ""); }
public string BuildSQL(string table, string columns, string wherePart) { return BuildSQL(table, columns, wherePart, ""); }
public string BuildSQL(string table, string columns, string wherePart, string orderBy) { return BuildSQL(table, columns, wherePart, orderBy, ""); }
public string BuildSQL(string table, string columns, string wherePart, string orderBy, string limit)
{
return String.Format("SELCT {0} FROM ...)", columns);
} allein um die einfachen Fälle abzudecken, bräuchtet Ihr 4 weitere Methoden für die Parameterüberladung, von den Fällen wie z.B. Tabelle und Order By mal abgesehen.
Wollen wir das? Nein, darum das ganze nochmal
public string BuildSQL(string table, string columns="*", string wherePart="", string orderBy="", string limit=""){ … }
BuildSQL("User");
BuildSQL("User", "Name");
BuildSQL("User", limit: "10");
BuildSQL("User", orderBy:"Name", limit:"20", columns:"Name"); Mit Hilfe der optionalen Parameter, brauchen wir jetzt nur noch eine Methode um das Ganze zu bewerkstelligen.
Wichtig Parameter die optional sein sollen, müssen unbedingt am Ende der Parameterliste stehen. Zusätzlich können wir in diesem Beispiel eine weitere Neuerung sehen.
Die benannten Parameter, helfen uns nicht nur beim Aufruf auf manche Parameter zu verzichten, sondern gibt es uns auch eine neue Art der Übersicht, so dass man sobald man sich den Aufruf anschaut, auf den ersten Blick sofort erkennen kann, welcher Wert in welchen Übergabe Parameter übergeben wird (natürlich nur solange eure Parameter eine sprechende Bezeichnung haben)
Was euch in diesem Beispiel ebenfalls auffallen sollte ist, dass die Reihenfolge wie man die Parameter angibt ist eigentlich nicht nur nicht relevant ist, sondern das die Reihenfolge wie man die Parameter angibt sogar beachtet wird.
static void Main(string[] args)
{
var p = new Program();
int n = 3;
p.WriteSomething(y:n++, x:n);
}
public void WriteSomething(int x, int y)
{
Console.WriteLine("x:{0}, y:{1}", x, y);
Console.ReadLine();
} Die Ausgabe lautet, für manch einen unerwartet
x:4, y:3 wie gesagt die Reihenfolge spielt eine Rolle
- erst wird dem Parameter y der Wert von n also 3 zugewiesen
- dann wird n Inkrementiert
- und erst dann dem x Parameter der inkrementierte Wert 4
An sich also ein schönes Feature, … doch wo liegt denn bei den optionalen Parameter eine Gefahr???
…
Um dieses Problem zu demonstrieren muss ich eine kleine Änderung an dem Projekt vornehmen.
Wir lagern die Methode inkl. der Defaultparameter in eine eigene DLL aus, kompilieren und schauen uns den erzeugten IL-Code an.
Hinweis:
Bisher, habe ich für solche Zwecke immer den Reflector benutzt, doch da Redgate sich dazu entschlossen hat, zukünftig 35 $ für dieses Produkt zu nehmen, gewöhne ich mich jetzt schon an die Alternative
ILSpy. Entwickelt wird dieses Tool von den gleichen Entwicklern die auch die IDE SharpDevelop hervorgebracht haben.
Dieser kleine Ausschnitt, zeigt uns nun den erzeugten IL-Code der Methode.

Das komische daran ist nur, dass ich an dieser Stelle weder etwas von den optionalen Parametern noch von den Defaultwerten entdecken kann.
(Hmm, was macht der Compiler denn da, oder habe ich die falsche DLL benutzt?)
Nein, die Assembly war auf jeden Fall die richtige
…
schauen wir uns einfach mal, die ausführbare Datei im ILSpy an (in der Hoffnung an das wir an dieser Stelle mehr sehen)
Also ILSpy starten, Exe laden und dann suchen wir mal die Stelle an der die Methode (z.B. BuildSQL) aufgerufen wird

So da haben wir ja zumindest schon einmal drei der Aufrufe
Wenn wir uns jetzt nochmal die C# Varianten in Gedächtnis rufen
//Zeile IL_0008 – IL_0026
BuildSQL("User");
//Zeile IL_0027 – IL_0046
BuildSQL("User", "Name");
//Zeile IL_0047 – IL_0064
BuildSQL("User", limit: "10");
in den Zeilen 8 – 26 erkennen wir, dass obwohl wir in unserm C# Code nur den Wert User an den Parameter table übergeben haben, zeigt der IL-Code das wir der Methode BuildSQL immer 5 Werte übergeben.
in diesem Beispiel können wir auch erkennen, was mit den Default werten passiert, Diese werden wie zb in der Zeile IL_000d bereits übergeben-
Fassen wir diese Erkenntnisse noch einmal kurz zusammen
- das Feature der benannten Parameter erlaubt es uns, Parameter beim Aufruf Namentlich zu benennen
- das Feature optionale Parameter, erlaubt es uns beim Aufruf einer Methode verschiedene Parameter nicht zu versorgen
- Defaultparameter erlauben es die Defaultwerte zu bestimmen, die immer dann benutzt werden wenn wir nichts übergeben
- Wenn man sich den erzeugten IL-Code anschaut, sieht man zumindest bei der Methode BuildSQL keine Veränderung im Gegensatz zu einer ‘normalen’ Methode
Wenn man sich den IL-Code anschaut, erkennt man jedoch, dass diese neuen Möglichkeiten sind nur syntaktischer Zucker sind, denn in Wirklichkeit übergeben wir immer jedem einzelnen Parameter einen Wert
Und genau an dieser Stelle, sieht der ein oder andere auch schon das Problem
Wenn wir nun eine Anwendung entwickeln, die ähnlich der Bespielapplikation aufgebaut ist. (eine ausführbare Datei und eine Assembly)
Hier befindet sich nun eine Methode X mit z.B. folgenden Parametern (string, string, bool=true) wobei der letzte Paramter ein Defaultparameter ist.
Die Applikation befindet sich bei einem Kunden im Einsatz, bis zu dem Tage an dem der Kunde anruft und einen Fehler meldet
Schnell kann man den Fehler in der Methode X ausfindig machen und stellt fest, der Defaultparameter müsste eigentlich false und nicht true sein.
Also ändert man den Defaultparameter und testet die nun veränderte Methode … Da wir Ihr ja nur die Library verändert haben, wird auch nur diese beim Kunden eingespielt
Jedoch beim Kunden ist dieser Fehler nicht verschwunden, … Ihr erinnert euch an diesen Blogeintrag und es fällt euch wieder ein …
Durch das Umsetzten des Defaultparameters, habt Ihr nicht unbedingt die Assembly verändert in der sich eure cs Datei befindet, sondern jedem einzelne Assembly die diese eine Methode aufruft!
Ein weiterer Bericht von der Basta Spring.
Diese Library gibt euch ein Vorgeschmack, auf das was in der nächsten Version des Framework kommen wird.
Die bisherigen Möglichkeiten einen Asyncronen Aufruf durchzuführen, ist mit den heutigen Mitteln zwar nicht besonders schwierig, jedoch schrecken manche Entwickler immer noch davor davor zurück, einen Vorgang asyncron durchzuführen.
Warum sollte ich Vorgänge asyncron durchführen?
Bei einem Vorgang der nur wenige Millisekunden dauert, muss ich den Vorgang nicht zwangsläufig asyncron durchführen, jedoch sobald der Vorgang länger dauert oder nicht abzusehen ist wie lang der jeweilige Vorgang dauern könnte, sollte man es machen.
Den Fakt ist, das User Interface sollte auch während einer länger laufenden Aktion, immer bedienbar bleiben!! den so ein Dialog verstärkt das Vertrauen eines Users nicht wirklich!
Wie funktioniert das ganze
Beispiel ohne Library am Beispiel des WebClient NS: System.Net
static void Main(string[] args)
{
string url = "http://www.Microsoft.de";
var client = new WebClient();
client.DownloadDataCompleted += client_DownloadDataCompleted;
client.DownloadDataAsync(new Uri(url));
//an dieser Stelle sind noch keine daten vorhanden
Console.ReadLine();
Console.WriteLine("<END>");
}
static void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
//jetzt kommen die Daten
string strData = Encoding.Default.GetString(e.Result);
Console.WriteLine(strData);
}
<pre>
Durch diese paar Zeilen Code würde die UI während des Herunterladen immer zugreifbar bleiben.
Beispiel mit Library
<span class="lnum"></span>
static void Main(string[] args)
{
DownloadData();
//an dieser Stelle sind noch keine daten vorhanden
Console.ReadLine();
Console.WriteLine("<END>");
}
private async static void DownloadData()
{
string url = "http://www.Microsoft.de";
var client = new WebClient();
//Zeile 16
string strData = await client.DownloadDataAsync(new Uri(url));
Console.WriteLine(strData);
}
An diesem Beispiel erkennen wir, das wir durch die beiden neuen Schlüsselwörter
async und Await das gleiche Ergebnis erreichen können.
Nur der Code sieht viel schöner aus!
Ja jetzt höre ich euch schon, was erreichen wir damit der Code bleibt an der Zeile 16
doch stehen bis er fertig ist, jedoch so ist es nicht.
Das Programm läuft genau so wie im ersten Beispiel.
DownloadData() wird bis zur Zeile 16 Ausgeführt und das Schlüsselwort Async bewirkt, dass das Programm
in der Main Methode solange Fortgesetzt wird bis die Daten heruntergeladen wurden.
Sobald die Daten vorliegen, läuft das Programm dann ab Zeile 16 weiter.
Link: Visual Studio Async CTP