Extension Methods

Mit Extension Methods können bestehende Typen mit zusätzlichen Methoden erweitert werden. Diese Methoden sehen aus wie Instanz-Methoden, sind jedoch statische Methoden einer Hilfsklasse.

Sie haben einen speziellen Syntax. Der erste Parameter einer Extension Methode kennzeichnet die jeweilige Klasse, die erweitert werden soll. Zudem muss er mit this markiert werden. Zudem muss die Klasse, welche die Extension Methode behinhaltet und auch die Methode statisch sein.

Beispie zur Erweiterung der string-Klasse:

public static class StringExtensions
{
public static bool StartsWithUpperCase(this string value)
{
if (value.Length > 0) return char.IsUpper(value[0]);
return false;
}
}

Danach kann die Methode benutzt werden:

string s = „Test“;
bool b = s.StartsWithUpperCase();

Was macht der Compiler im Hintergrund? Schauen wir uns das Code-Stück davon mal in IL-Code an:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 20 (0x14)
.maxstack 1
.locals init ([0] string s)
IL_0000: nop
IL_0001: ldstr „Hallo“
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call string MyApp.StringExtensions::TimWhiteSpaces(string)
IL_000d: callvirt instance string [mscorlib]System.String::ToUpper()
IL_0012: stloc.0
IL_0013: ret
} // end of method Program::Main

Die Extension Methode wird ganz normal als Klassenmethode aufgerufen. War ja nicht anders zu erwarten. Die Extenstion Methoden sind somit nur eine Hilfe für faule Programmierer :-).

Method Chaining

In vielen Fällen ist es hilfreich, wenn man das Method Chaining unterstützt. Dies bedeutet, dass man anstelle von void den jeweiligen Typen wieder zurückgibt.

public static class StringExtensions
{
public static string TimWhiteSpaces(this string value)
{
return value.Trim(‚ ‚, ‚\t‘, ‚\r‘, ‚\n‘);
}
}

Somit können auf dem Rückgabe-Objekt weitere Methoden aufgerufen werden:

string s = „\tTest  „;
s = s.TimWhiteSpaces().ToUpper();

Meine Empfehlung

Extension Methods sind cool – nichts desto trotz verschlechtern sie meines Erachtens eher das Software-Design. Im Hintergrund bleibt alles beim Alten. Es wird dem Entwickler einfach vorgegaukelt, es handle sich um eine Instanzmethode. Zudem kann es häufig der Fall sein, dass man in einem Modul eine Extension Methode benutzen möchte (an welche man sich gewöhnt hat), diese jedoch nicht auffindbar ist. Dann geht die Suche los, in welche Klasse sich diese Methode befinden, damit man sie inkludieren und benutzen kann. Soweit ich weiss, hat Microsoft die Extension Methoden eingeführt, damit LINQ gut ins Framework integriert werden konnte. Ich empfehle somit, alle eigenen Extenstion Methoden in ein spezielles Assembly auszulagern und nur zu verwenden, wenn es die Arbeit massiv erleichtet.
Veröffentlicht unter Coding | Verschlagwortet mit ,

Lambda Expressions

Die Lambda Expressions sind ein neues Feature von C# 3.0. Sie stellen Funktionen dar, die zur Laufzeit bereits kompiliert worden sind oder kompiliert werden können. Überall wo man in C# 2.0 eine Delegate schreiben musste, kann man in C# 3.0 auch eine Lambda Expressions benutzen.

Ohne grosse verbale Abhandlungen, versuche ich den Syntax von Lambda an ein paar einfachen Beispielen zu erklären…

Beispiel einer Delegate mit einem Parameter:

delegate int Calculator(int i);
Calculator square = delegate(int x) {return x * x;};
int result = square(3);   // Ergibt 9

Beispiel einer Lambda-Funktion mit einem Parameter

delegate int Calculator(int i);
Calculator square = x => x * x;
int result = square(3);   // Ergibt 9

Beispiel mit zwei Parameter:

delegate int Calculator(int i, int k);
Calculator multiply = (x,y) => x * y;
int result = multiply(3,4);   // Ergibt12

Beispiel einer Operation ohne Parameter

delegate int Sequence();
int seed = 0;
Sequence counter = () => seed++;
counter();   // seed ist 1
counter();   // seed ist 2

Wie man erkennen kann, beschreibt die Definitionen vor dem Lambda-Operator „=>“ die jeweiligen Parameter. Formal betrachtet könnte man den Lambda-Syntax folgendermassen beschreiben:(input parameters) => expression resp. (input parameters) => {statement;}

Wie man sieht, sind Lambda Funktionen und Expressions sind extrem allgemein gehalten. Wie man Lambda-Ausdrücke dynamisch on-the-fly erzeugen kann, werde ich in einem späteren Artikel erklären. Dies ist dann schon ein wenig komplexer.

Das generische Func<> delegate

Wie man oben gesehen hat, werden Lambda-Ausdrücke in delegates übersetzt, um ausgeführt zu werden. Um nicht für jede Funktion eine delegate zu deklarieren (wie oben), gibt es neu die generischen Func<> delegates. Damit lassen sich die eigenen Deklarationen zu einem grossen Teil eleminieren und muss nicht auf die Typsicherheit verzichten.

Beispiel:

Func<int, int> square = x => x * x;
int result = square(3);   // Ergibt 9

Folgende 5 verschiedene Func<> delegates gibt es:

public delegate TResult Func<TResult>();
public delegate TResult Func<T, TResult>(T arg);
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);

Damit lassen sich somit Funktionen bis zu 4 Parameter abdecken, ohne eigene Delegates zu deklarieren.

Das generische Action<> delegate

Das generische Action<> delegate ist dem Func<> sehr ähnlich. Der einzige Unterschied befindet sich darin, dass Func<> eine Funktion ist und einen Rückgabewert besitzt. Der Rückgabewert von Action<> ist immer void.

Beispiel:

Action<string> writeCons = x => Console.WriteLine(x.ToString());
writeCons(„Test“);

Die 4 verschiedenen Action<> delegates lassen vermuten, dass es sich hierbei auch wiederum um eine Vereinfachung für den Entwickler handelt. Delegates bis zu 4 Parameter braucht man nicht mehr selber zu deklarieren, sondern kann die generischen Standard-Delegates vom System-Namespace nehmen.

public delegate void Action<T>(T obj);
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);

Wichtiger Hinweis

Anfänglich ist der gebraucht von Lambda extrem umständlich und verwirrend. Der Quellcode sieht zu beginn auch sehr befrendend, seltsam und unübersichtlich aus. Doch das ist normal und man gewöhnt sich recht schnell daran (…wie ehem. VB-Programmierer an die C#-Klammern – gell Urs!). Lambda ist meines Erachtens eine super Sache und erleichtert die Programmierung und Übersicht merklich. Zudem sind Lambda-Ausdrücke und das Verständnis, wie sie funktionieren für LINQ unabdingbar. Ohne Lambda kein LINQ!

Veröffentlicht unter Coding | Verschlagwortet mit ,

Der neue Donnervogel

Thunderbird 3.0 von Mozilla ist in der Pipeline und soll voraussichtlich im Feb. 2009 erscheinen.

Die neue Version soll diverse neue Features und Verbesserungen vorweisen:

  • Integrierter Kalender (auf der Basis des Lightning Plugins)
  • Tabbed Mail Browsing
  • Verbesserte Suche
  • Verbesserter Feed Reader
  • Vereinfachtes Userinterface
  • Erweiterte Optionen in der Sicherheit (Verschlüsselung, Phishing, Junk Filter)

Ich bin sicher, dass die grosse Stärke von Thunderbird nicht in den neuen Features, sondern in der neuen vereinfachten PlugIn-Möglichkeiten liegen. Sollte sich Thunderbird langsam zu einem kleinen Outlook-Konkurrenten mausern, wäre es die ideale Ergänzung zum neuen OpenOffice, denn genau das ist der fehlende Teil vom Software-Paket.

Ich als begeisterter Thunderbird-Benutzer freue mich auf die neue Version!

C# Basics – letzter Teil

Der letzten Teil der Basics fällt eher mager aus…

Die Environment Klasse

Die Environment Klasse bietet nicht nur das bekannte NewLine an, sondern noch diverse andere hilfreiche Eigenschaften:

CurrentDirectory
Kompletter Pfad des Arbeitsverzeichnisses des Prozesses.
SystemDirectory
Kompletter Pfad zum Systemverzeichnis.
CommandLine
Kommandozeile des Prozesses.
MachineName
Beinhaltet den NetBIOS Namen der Maschine.
ProcessorCount
Beinhaltet die aktuelle Anzahl Prozessoren des Systems.
OSVersion
Beinhaltet das aktuelle OperatingSystem Objekt.
ExitCode
Der Exit-Codes des Prozesses.
HasShutdownStarted
Beinhaltet den Wert, ob die CRL oder die Applikation gerade heruntergefahren wird.
StackTrace
Beinhaltet die aktuellen StackTrace Informationen.
TickCount
Ermittelt die Anzahl Millisekunden seit Systemstart.
UserDomainName
Name der Netzwerk-Domäne in welcher der Benutzer angemeldet ist.
UserInteractive
Indikator, ob der aktuelle Prozess im interaktiven Modus ausgeführt wird. Ein Prozess, der im IIS gehostet wird, würde false zurückgeben, da keine Benutzerinteraktion aktiv ist.
UserName
Benutzernamen der Person ab, die derzeit beim Windows-Betriebssystem angemeldet ist.
WorkingSet
Größe des physischen Speichers ab, der dem Prozesskontext zugeordne ist.
Version
Beinhaltet das Version Objekt von der Applikation. Daraus lassen sich Major, Minor, Build und Revision Nummern lesen.

Seltene Operatoren

Folgende Operatoren sind in den meisten C# Programmen eher seltene Gäste, weil sie in den meisten Fällen nur in speziellen Situationen gebraucht werden.

-> : Pointer to struct (unsafe)
Wird benutzt, um an die Methoden eines Pointers zu kommen
Beispiel: p->ToString();   // wenn p ein Pointer in einem unsafe-Block ist

* : Value at address (unsafe)
Wird benutzt, um an den Wert an einer Memory-Adresse zu kommen
Beispiel: int a = *p;   // wenn p ein Int-Pointer in einem unsafe-Block ist

& : Address of value (unsafe)
Wird benutzt, um an die Memory-Adresse eines Objektes zu kommen
Beispiel: int* p = &i;   // wenn i ein int Objekt ist

~ : Bitwise complement
Wird benutzt, um an den Komplementärwert zu kommen
Beispiel: int a = ~0;   // Ergibt das Komplement -1

% : Remainder
Wird benutzt, um den Restwert einer Division zu erhalten
Beispiel: int a = 4 % 3   // Ergibt den Restwert 1

<< oder >> : Shift left oder right
Wird benutzt, um einen Wert bitweise nach links oder rechts zu verschieben
Beispiel: int a = 8 << 2;   // Ergibt 32 (8 wird bitweise 2 mal nach links verschoben)

^ : Logical Xor
Wird benutzt, um einen Wert bitweises exklusives ODER zu rechnen
Beispiel: int a =  15 ^ 3; // Ergibt 12 oder binär 1100

?? : Null Coalescing
Wird benutzt, um den linken Operanden zurückgegeben, falls dieser nicht null ist. Ansonsten wird der rechte Operand zurückgegeben.
Beispiel: object x = null ?? new object();   // x wird das neue Objekt enthalten, da der erste Operand null war

?: : Conditional
Wird benutzt, um abhängig vom Wert eines booleschen Ausdrucks einen von zwei Werten zurück
Beispiel: int a = 1 == 3 ? 10 : 20;   // Ergibt 20, da die Bedinung 1 == 3 falsch ist

Links, die man immer wieder braucht

Es gibt Links in die MSDN, die man immer wieder nachschlagen muss, weil gewisse Dinge einfach lange nicht benutzt werden. Hier eine kleine Sammlung davon:

Sprache C#

C# Schlüsselwörter

C# Operatoren

C# Präprozessordirektiven

C# Compileroptionen

Formatierungen

Zahlen-Formatzeichenfolgen (standard)

Zahlen-Formatzeichenfolgen (benutzerdefiniert)

DateTime-Formatzeichenfolgen (standard)

DateTime-Formatzeichenfolgen (benutzerdefiniert)

Enumerations-Formatzeichenfolgen

Veröffentlicht unter Coding | Verschlagwortet mit ,

C# Basics Teil 4

Hier der vierte Teil zu den Basics…

Nullbare Typen

Es ist allgemein bekannt, dass man Value-Typen nicht auf null prüfen oder damit initialisieren kann, da es sich ja nicht um Referenzen handelt. Nun, das ist grundsätzlich immer noch richtig. Doch C# hat ab 2.0 ein neues Feature eingebaut, damit man jede Art von Value-Typen “nullen” kann :-). Der Gebraucht ist sehr simpel. Jeder Typ kann mit einem Postfix in Form eines “?” zu einem nullbaren Typen gemacht werden.

Beispiel:

int a = null; // Kompiler-Error
int? b = null; // OK
if (b == null) { b = 1; }
if (b > 1) { b = 2; }

Warum funktioniert das?

Die Lösung des Rätzels ist einfach. Der C# Kompiler bedient sich einem neuen generischen Typen Nullable<T>. Bei der Kompilation wird der int? einfach in einen Nullable<int> umgewandelt. Ein int? ist so gesehen also kein wirklicher int mehr, sondern eine Instanz von Nullable, die den int hostet.

Man könnte sich nun fragen, warum dann ein Vergleich wie b > 1 noch funktioniert. Wenn der int? ja zu Nullable<int> transformiert wurde, dürfte ein Vergleich mit einem int nicht mehr funktionieren. Zudem Implementiert Nullable<> keine Operatoren <, > oder ==. Auch hier hat der Kompiler seine Hand im Spiel. Man nennt das ganze “Lifted Operators”. Der Kompiler klaut (oder hebt) den jeweiligen Operator vom darunterliegenden Typen an und schreibt einen neuen Ausdruck.

Beispiel:

int? a = 10; // OK
bool b = a > 5;

wird vom Kompiler zu folgendem Ausdruck übersetzt:

Nullable<int> a = new Nullable<int>(10);
bool b = a.HasValue ? a.Value > 5 : false;

Hinweis:

Vorsicht beim Vergleichen von Typen.

Nullable<int> a = 10;   // oder int?
bool isInt = a.GetType().Equals(typeof(int));   // true
bool isNullable = a.GetType().Equals(typeof(Nullable<int>));  // false!!!

Man sieht, obwohl die Variable a als Nullable<int> deklariert wurde, wird ein typeof(Nullable<int>) nicht funktionieren. Warum: a ist eine Instanz von Nullable<int>.  Der Kompiler “liftet” in diesem Falle die Operatoren mit denjenigen vom darunterliegenen Typen… und so auch den typeof-Operator!

Aber Vorsicht, es handelt sich dabei nie um einen wirklichen int. Prüft man die Instanz z.B. auf die Implementation eines IConvertible-Interface, wird dieser Test mit false quittiert, weil Nullable zwar die Operatoren liftet, jedoch die Schnittstellen der darunterliegenden Klasse in Ruhe lässt. Obwohl es bei der Konvertierung eigentlich keine Probleme geben sollte, gibt es im Framework eine spezielle Klasse dafür mit dem Namen NullableConverter.

Das Schlüsselwort fixed

Den meisten wird das Schlüsselwort fixed noch nie begegnet sein. Warum ist einfach zu erklären. Hierbei handelt es sich um eine Spezialität im Bereich von unsicherem Code.

Wozu dient es: Der GC (garbage collector) schiebt aus Effizienz- und Platzgründen während der Programmausführung die Objekte im Speicher hin und her und das Memory fragmentiert. Dies kann in gewissen Fällen Proleme verursachen, wenn man in einem mit unsafe markierten Codestück gerade dabei ist über die Memory-Adressen zu loopen. Mit dem fixed-Statement teilt man dem GC mit, dass er die Objekte darin in Ruhe lassen soll. Dies steigert zudem die Effizienz beim Durchlauf.

Beispiel:

unsafe
{
string myString = “Hallo”;
fixed (char* p = myString) { *p = ‘A’; }
}

Das Objekt myString wird vom GC innerhalb des fixed-Blockes in Ruhe gelassen. Man beachte zudem die Zuweisung der Adresse vom myString. Diese Zuweisung braucht keinen Adress-Operator “&”, da es sich beim String-Objekt ja bereits um eine Referenz handelt.

Veröffentlicht unter Coding | Verschlagwortet mit ,