Concatenation of Strings in C#
0102030405
string tmp = "";
tmp += "ersteZeichenkette";tmp += "zweiteZeichenkette";tmp += "dritteZeichenkette";
StringBuilder
-Klasse:
0102030405060708
StringBuilder tmpBuilder = new StringBuilder();{ tmpBuilder.Append("ersteZeichenkette"); tmpBuilder.Append("zweiteZeichenkette"); tmpBuilder.Append("dritteZeichenkette");}
string tmp = tmpBuilder.ToString();
StringBuilder
bis zu zwei Größenordnungen mehr Vorgänge in der gleichen Zeit wie per Operator möglich sind. Zudem wird selbstverständlich deutlich weniger Arbeitsspeicher benötigt. Insbesondere bei umfangreichen Schleifen ergibt sich hier also sehr schnell ein ernstzunehmendes Einsparpotential. Es handelt sich keinesfalls um "Mikrooptimierung" und wird auch von Microsoft selbst betont.Bei ganz einfachen Verknüpfungen mit zwei Zeichenketten verwende ich durchaus die Operator-Variante, da sie etwas kompakter ist und der Unterschied minimal. Sobald mehrere Verknüpfungen notwendig werden, setze ich für gewöhnlich jedoch bereits auf einen
StringBuilder
.Die intern erzeugte Zeichenkette des
StringBuilder
kann zur weiteren Verwendung wie im Beispiel ersichtlich jederzeit per ToString()
abgerufen werden. Darüber hinaus stellt diese Klasse auch noch ein paar komfortable Methoden wie beispielsweise AppendLine(…)
zur Verfügung. Versionierung
Das möchte ich mit diesem Beitrag gerne machen, jedoch gehe ich da ziemlich traditionell und wenig spektakulär vor, so dass ich keine allzu großen Hoffnungen auf bahnbrechende Erkenntnisse machen möchte.

Vor der Fertigstellung der initial zu veröffentlichenden Version verwende ich lediglich die internen Versions- bzw. Buildnummern. Zur öffentlichen Versionsnummerierung halte ich mich üblicherweise an folgendes Schema:
x.y.z
-
x: Hauptversion
Diese Nummer wird bei einer kompletten Neuentwicklung, bzw. einem Technologiewechsel erhöht. Dies ist z. B. bei einem Wechsel der Programmiersprache der Fall, oder aber wenn strukturelle/inkompatible Änderungen (insbesondere an öffentlichen Schnittstellen) stattfinden. Damit einher geht jedoch neben der – durchaus einschneidenden – Änderung an sich auf jeden Fall auch eine Neuentwicklung wesentlicher Bestandteile des gesamten Projektes.
-
y: Funktionalitätserweiterung
Diese Nummer wird bei Erweiterungen der Funktionalität einer Anwendung erhöht. Dazu werden einige – im Allgemeinen überschaubare – zu erledigende Aufgaben gesammelt und dann mit dieser Version veröffentlicht. Bei besonders umfangreichen Ergänzungen kann diese Veröffentlichung durchaus auch nur einen einzelnen Aspekt betreffen, andererseits aber eben auch sehr viele kleinere.
-
z: Fehlerbehebung, Optimierung
Diese Nummer wird bei der Behebung von Fehlern einer Anwendung erhöht. Dazu werden im Allgemeinen einige (insbesondere weniger dramatische) Probleme gesammelt und dann mit dieser Veröffentlichung behoben. Bei kritischen Fehlern, die die Funktionsfähigkeit des gesamten Programmes verhindern, oder aus Sicherheitsgründen schnellstmöglich behoben werden müssen, kann eine solche Version auch nur für einige wenige Änderungen für ein einzelnes Problem veröffentlicht werden.
Sofern bestehende Funktionalität zwar erwähnenswert optimiert wird, jedoch nicht grundlegend erweitert, erscheint mir diese Stelle ebenfalls angemessen.
Dynamic Typing in C#
Seit .NET 4.0 enthält das Framework die sog. Dynamic Language Runtime, was sich bei C# auf den ersten Blick durch das neue Schlüsselwort
dynamic
, die Basisklasse DynamicObject
und das ExpandoObject
mit entsprechender Funktionalität bemerkbar macht.Die DLR ermöglicht zwar "auch" interessante Aspekte im Hinblick auf die Integration dynamisch typisierter Programmiersprachen wie beispielsweise Python (→ IronPython) oder Ruby (→ IronRuby) in das .NET-Framework, ich möchte in diesem Beitrag aber dennoch bei den Möglichkeiten in C# selbst bleiben. Ein paar Gedanken meinerseits zur Typisierung im Allgemeinen sollen in einem späteren Beitrag folgen.
Wie vielen Lesern dieses Blogs bekannt sein dürfte, handelt es sich bei C# um eine grundsätzlich statisch typisierte Programmiersprache, d. h. die Typen aller Variablen sind bereits zum Zeitpunkt der Kompilierung bekannt, was nicht zuletzt auch ermöglicht, dass euch Visual Studio hilfreiche Hinweise schon während der Entwicklung geben kann.
Durch die DLR wird von diesem Prinzip (glücklicherweise

dynamic
deklariert. Dadurch wird die statische Typprüfung deaktiviert und auf die Laufzeit verschoben. Das bedeutet, dass ihr einer Variable dieses Typs beliebige – also auch gar nicht existente – Eigenschaften zuweisen oder darauf Methoden aufrufen könnt, im Problemfall jedoch zur Laufzeit eine entsprechende Ausnahme erhaltet.Wir beginnen – wie üblich – mit einem kleinen Code-Beispiel zur Verdeutlichung: Nun können wir relativ "normal" damit arbeiten, beispielsweise die Werte in einer MessageBox ausgeben:
0102030405060708
MessageBox.Show(a.ToString()); //: "123"MessageBox.Show(b.ToString()); //: "123"/** * C# konvertiert hier nicht in eine mathematische Addition, * da "b" zur Laufzeit als Zeichenkette interpretiert wurde.**/MessageBox.Show(a + b); //: "123123"
0102
MessageBox.Show(b.Replace("2", "4")); //: "143"MessageBox.Show(a.Replace("2", "4")); // Ausnahme!
Replace(…)
für den Datentyp int
nicht zur Verfügung steht (bzw. erst gar nicht zur Auswahl angeboten) und die Kompilierung mit einem Fehler abgebrochen.Im Gegensatz zu
var
kann sich der Typ unserer Variable nun auch während der Laufzeit ändern:
01020304050607
MessageBox.Show(a.GetType().ToString()); //: "System.Int32"MessageBox.Show(a.ToString()); //: "123"
a = "test";
MessageBox.Show(a.GetType().ToString()); //: "System.String"MessageBox.Show(a.ToString()); //: "test"
Wenn das nun neben ein bisschen Bequemlichkeit beim Tippen doch eher Probleme einführt, stellt sich natürlich die berechtigte Frage, wozu man so etwas verwenden sollte. Hauptsächlich handelt es sich tatsächlich um ein Mittel, dynamische Sprachen in das .NET-Framework zu integrieren, welche nun einmal auf diesem Konstrukt basieren. Auch die Verwendung entsprechender Programmbibliotheken, um beispielsweise einfach über XML-Knoten zu navigieren (man denke an dieser Stelle auch an das DOM) sind sicherlich eine praktikable Idee. Für die reine Bequemlichkeit ist implizite Typisierung jedoch definitiv besser geeignet.
Ich würde auf jeden Fall dringend davon abraten, unter C# selbst exzessiven und vor allem unüberlegten Einsatz davon zu machen. Natürlich kann man sich dadurch z. B. gewisse Umstände, die Reflektion mit sich bringt, ersparen, dennoch würde ich hier nach wie vor zu einem sauberen, auf Schnittstellen basierten Modell raten. Dadurch, das alles "irgendwie" aufgerufen werden kann, handelt man sich nicht selten schwer auffindbare und auch lösbare Fehler ein. Insbesondere sollte bedacht werden, dass das .NET-Framework beim Aufruf entsprechender Funktionalität auch immer passende Datentypen erwartet; Folgendes ergibt beispielsweise umgehend einen Laufzeitfehler:
010203
dynamic c = 5;
MessageBox.Show(c); // Ausnahme!
Show(…)
eine Zeichenkette erwartet; bei b
hätte es auch so funktioniert, c
muss jedoch zwingend mit ToString()
aufgerufen werden. Mit statischer Typprüfung sind das alles Probleme, die man bereits beim Schreiben des Quelltextes erfährt. Mit dynamischer Typprüfung möglicherweise aber erst nach einigen Stunden oder gar noch längerer Laufzeit, wenn die Stelle zum ersten Mal zur Ausführung kommt.Da wir nun umfassend auf die potentiellen Gefahren eingegangen sind und erste Beispiele betrachtet haben, möchte ich noch einmal kurz auf
DynamicObject
und ExpandoObject
zurückkommen und dazu jeweils ein kleines Anwendungsbeispiel demonstrieren. Wir beginnen mit dem ganz einfachen ExpandoObject
und fügen diesem Eigenschaften und Verhalten hinzu:
01020304050607080910111213141516
dynamic obj = new ExpandoObject();{ // Eigenschaften setzen obj.FirstName = "Holger"; obj.LastName = "Stehle";
obj.Age = 24;}
// Verhalten definierenobj.GetName = new Func<string>(() => { // der Rückgabetyp könnte auch "dynamic" lauten return obj.LastName + ", " + obj.FirstName;});
// Name und Alterstring person = obj.GetName() + " (" + obj.Age + ")"; //: "Stehle, Holger (24)"
DynamicObject
werfen, von welchem wir erben und dabei spezifisches Verhalten für bestimmte Aktionen festlegen können. Konkret möchten wir den Zugriff auf Eigenschaften unabhängig von Groß-/Kleinschreibung ermöglichen:
01020304050607080910111213141516171819202122232425262728293031
// Klasse ableiten und Verhalten anpassenpublic class MyDynamicObject : DynamicObject { private Dictionary<string,object> data = new Dictionary<string,object>();
// Eigenschaft abrufen public override bool TryGetMember(GetMemberBinder binder, out object value) { return data.TryGetValue(binder.Name.ToLower(), out value); }
// Eigenschaft setzen public override bool TrySetMember(SetMemberBinder binder, object value) { data[binder.Name.ToLower()] = value;
return true; }}
// Klasse verwendendynamic obj = new MyDynamicObject();{ // Eigenschaften setzen obj.FirstName = "Holger"; obj.lastName = "Stehle";}
string firstName = obj.FirstName; //: "Holger"string firstName = obj.firstName; //: "Holger"string lastName = obj.lastName; //: "Stehle"string lastName = obj.LastName; //: "Stehle"
Wie üblich hält das MSDN weitere Hinweise zur Verwendung parat.
Representation of IP Addresses (Appendix: Version numbers)
1.2.3
) anzuwenden, welche schließlich ähnlich aussehen, wie eine IP-Adresse.Grundsätzlich ist das natürlich naheliegend und nicht völlig abwegig, funktioniert jedoch nur bedingt. Das Problem: Die Ziffern unserer Stellen müssten nach bisherigem Vorgehen endlich sein, damit wir die Basis korrekt wählen können, um das Ergebnis eindeutig zu halten. Im Dezimalsystem stehen uns als Ziffern
0
– 9
zur Verfügung. Bei IP-Adressen lauten die "Ziffern" 0
– 255
(dezimal).Bei einer Versionskennung muss im Allgemeinen beispielsweise auf v
1.0.9
nicht zwangsläufig v1.1.0
folgen (da die einzelnen "Stellen" traditionell* als Hauptversion, Nebenversion und Fehlerbehebungsversion geführt werden). Das heißt, werden erneut lediglich einige Fehler behoben, wird eher v1.0.10
folgen usw. Bei anders aufgebauten Versionskennungen (variierende Stellenanzahl, "beta" etc.) wird es noch schwieriger.Sollte jedoch, aus welchen Gründen auch immer, jede Stelle auf z. B.
0
– 9
begrenzt sein, so könnten wir natürlich analog zum "üblichen" Dezimalsystem verfahren und als Basis die 10
wählen (oder einfach die Punkte entfernen …), womit das Verfahren wieder funktioniert. Dies dürfte jedoch ein eher seltener und ungewöhnlicher Spezialfall sein, insofern sollte hier der Einfachheit wegen nach wie vor eher stellenweise verglichen werden.*) Ich persönlich halte daran auch fest. Viele Projekte scheinen diesem Schema jedoch nicht mehr zu folgen. Teilweise gibt es zumindest zur Kommunikation nach Außen gar nur noch eine einzelne Versionsnummer (z. B. v
19
), was jedoch auf den ersten Blick weniger über den Inhalt der Version aussagt. Es ist sicherlich nicht ganz falsch, dass das den Endanwender auch nicht direkt interessieren muss – praktisch finde ich es trotzdem. 
Representation of IP Addresses
Jeder hat bestimmt schon einmal IP-Adressen der Form "127.0.0.1" (lokale Adresse), "192.168.1.1" (private Adresse) oder "217.160.176.125" (öffentliche Adresse) gesehen. Das ist eine für den Menschen bequeme Schreibweise, jedoch nur bedingt zur automatisierten Verarbeitung (z. B. für Vergleiche bei Sortierungen) geeignet.
Eine IPv4-Adresse besteht aus vier Segmenten, die jeweils 8 Bit (≙ 4 * 1 Byte = 32 Bit, bzw. 4 Byte) umfassen. Insofern verwendet eine IPv4-Adresse dieser Schreibweise ("dotted decimal") eine Art Stellenwertsystem wie unser gebräuchliches Dezimalsystem, wobei die einzelnen Stellen jedoch mehrere Ziffern umfassen und daher durch ein Trennzeichen (in diesem Fall ein ".") getrennt werden.
Die Wertigkeit der Stelle einer Zahl unseres Dezimalsystems ergibt sich bekanntermaßen aus dessen Position:
01020304050607
1 = 1 * 100 5 = 5 * 100
10 = 1 * 101 + 0 * 100 15 = 1 * 101 + 5 * 100
123 = 1 * 102 + 2 * 101 + 3 * 100
0102030405
127. 0. 0. 1 = 127 * 224 + 0 * 216 + 0 * 28 + 1 * 20 = 2 130 706 433
192.168. 1. 1 = 192 * 224 + 168 * 216 + 1 * 28 + 1 * 20 = 3 232 235 777
82.165. 40.206 = 82 * 224 + 165 * 216 + 40 * 28 + 206 * 20 = 1 386 555 598
Die zu dieser Thematik passende Eigenschaft
Address
der IPAddress
-Klasse des .NET-Frameworks ist als veraltet gekennzeichnet. Solltet ihr diese Funktionalität dennoch benötigen, so lässt sich dies selbstverständlich auch schnell selbst schreiben – wobei zu berücksichtigen gilt, dass das nur für IPv4-Adressen in dieser Form verwendet werden kann:
010203040506
IPAddress ipAddress = IPAddress.Parse("217.160.176.125");byte[] segmentList = ipAddress.GetAddressBytes();
long ip2long = Convert.ToInt64((segmentList[0] * Math.Pow(2, 24)) + (segmentList[1] * Math.Pow(2, 16)) + (segmentList[2] * Math.Pow(2, 8)) + segmentList[3]); // – oder auch -long ip2long = (((long) segmentList[0] << 24) | ((long) segmentList[1] << 16) | ((long) segmentList[2] << 8) | (long) segmentList[3]);
ip2long
(und deren Gegenstück long2ip
) an. Project links
-
BitStadt – Stadtportal
Berlin · Hamburg · Amsterdam -
CCC – Fahrplan
Schedules for the CCCongress
Categories / Archive | Übersicht
- PHP functions in C# (136)
- Dictionary (257)