Schlagwort: Generika

Typisierung 👍 👎

Ein "beliebtes" (Streit-)Thema in der Softwareentwicklung sind die verschiedenen Typsysteme von Programmiersprachen. Grundsätzlich dienen diese hauptsächlich dazu, den Wertebereich von Variablen sinnvoll einzuschränken – idealerweise, um mögliche Fehler zu vermeiden. Vorteilhaft ist meist auch eine deutlich umfassendere Hilfestellung der Entwicklungsumgebung.

Nun gibt es verschiedene Möglichkeiten, ein solches System konkret zu implementieren. Dieser Beitrag soll explizit nicht dazu dienen, das eine System zu verteufeln und das jeweils andere hochzuloben. Dennoch haben die meisten Entwickler – so auch ich – eine gewisse Präferenz, sofern die Situation kein besonderes Vorgehen erfordert und man in der Wahl somit weitestgehend frei ist. Natürlich haben jeweils "beide Welten" ihre Vor- und Nachteile.

Dazu möchte ich einige Gegenüberstellungen mit Beispielen präsentieren, deren Definitionen jedoch nicht unbedingt als allgemeingültige Aussage zu verstehen sind, sondern vielmehr vom Kontext abhängig sein können:

Dynamisch vs. Statisch

Bei der statischen Typprüfung sind die Typen bereits zum Zeitpunkt der Entwicklung bekannt und werden entweder explizit vom Entwickler angegeben, oder aber implizit vom System abgeleitet (mehr dazu im nächsten Abschnitt). Bei der dynamischen Typprüfung findet die Prüfung der Typen erst zur Laufzeit statt.

  • Dynamisch: z. B. C++ (optional), C# (optional), JavaScript, Lua, PHP, Prolog, Python, Ruby, Scheme
    Beispiele in C# und PHP
    01020304050607
      // C#dynamic age = 27;              // zur Laufzeit als "int" erkanntdynamic firstName = "Holger";  // zur Laufzeit als "string" erkannt
    // PHP$age = 27; // zur Laufzeit als "int" erkannt$firstName = "Holger"; // zur Laufzeit als "string" erkannt
  • Statisch: z. B. Ada, C++ (standardmäßig), C# (standardmäßig), Haskell, Java
    Beispiele in C++ und C#
    01020304050607
      // C++int age = 27;                      // Ganzzahlstd::string firstName = "Holger";  // Zeichenkette
    // C#int age = 27; // Ganzzahlstring firstName = "Holger"; // Zeichenkette

Ich bevorzuge hier, sofern die Umstände nicht dagegen sprechen, die statische Typisierung. Für mich überwiegt hier der Vorteil der Typprüfung zur Entwicklungs- bzw. Kompilierzeit. Viele Fehler, die oftmals erst während der Laufzeit auffallen, können so effektiv vermieden werden. Durch Konzepte wie die generische Programmierung kann hier der notwendige Mehraufwand zwar deutlich reduziert werden, dennoch erfordert eine statische Typisierung in der Regel (minimal) mehr Aufwand bei der Entwicklung und kann mitunter umständliche Konstrukte bei sehr dynamischer Datenverarbeitung – beispielsweise im Web-Bereich – erforderlich machen; s. Dynamische Typisierung in C#.

Explizit vs. Implizit

Bei der expliziten Angabe der Typen trägt man als Entwickler Verantwortung dafür, den zum angedachten Inhalt einer Variablen (oder Rückgabe einer Methode) passenden Typen auszuwählen. Bei impliziter Ableitung erledigt dies das Typsystem, indem es den passenden Datentyp zum Inhalt annimmt.

  • Explizit: z. B. Ada, C++, C# (standardmäßig), Java
    Beispiele in C++ und C#
    01020304050607
      // C++int age = 27;                      // Ganzzahlstd::string firstName = "Holger";  // Zeichenkette
    // C#int age = 27; // Ganzzahlstring firstName = "Holger"; // Zeichenkette
  • Implizit: z. B. C# (optional), Haskell, JavaScript, Lua, Prolog, Python, Ruby, PHP, Scheme
    Beispiele in C# und PHP
    01020304050607
      // C#var age = 27;              // Ganzzahlvar firstName = "Holger";  // Zeichenkette
    // PHP$age = 27; // Ganzzahl$firstName = "Holger"; // Zeichenkette

Ich bevorzuge hier, sofern die Umstände nicht dagegen sprechen, die explizite Typangabe. Diese bedeutet üblicherweise zwar etwas mehr Schreibaufwand, erhöht aus meiner Sicht jedoch die Verständlichkeit des Quelltextes, da ich die Bezeichner von Variablen ungerne mit Präfixen oder Suffixen versehe, sondern mich bei der Benennung auf den Inhalt bzw. Zweck konzentrieren möchte. Um eine Kleinigkeit zu testen oder für ansonsten sehr umständliche Konstrukte (vgl. generische Programmierung) empfinde ich implizite Typableitung jedoch durchaus komfortabel. Teilweise kann dies sogar notwendig sein; s. Implizite Typisierung in C#.

Stark vs. Schwach

Dieser Aspekt behandelt im erweiterten Sinne die Typsicherheit des Typsystems. Stark typisierte Programmiersprachen erlauben für gewöhnlich – wenn überhaupt – nur implizite Typumwandlungen, sofern kein Datenverlust auftritt (beispielsweise von einem ganzzahligen Datentyp kleineren Wertebereiches zu einem mit größerem Wertebereich). Schwächer typisierte Programmiersprachen sind hier für gewöhnlich "großzügiger".

  • Stark: z. B. Ada, C++, C#, Haskell, Java, Python, Scheme
    Beispiele in C#
    01020304050607
    int pi = 3.14;    // Kompilierungsfehler!pi = (int) 3.14;  // 3
    int tmp = 27;
    if(tmp) { // Kompilierungsfehler! Debug.WriteLine("klappt!");}
  • Schwach: z. B. JavaScript, Lua, Prolog, Ruby, PHP
    Beispiel in PHP
    0102030405
    $tmp = 27;
    if($tmp) { // implizite Auswertung als "true" echo 'klappt!';} // Ausgabe von "klappt!", da Zahlen ungleich "0" als "true" ausgewertet werden

Ich bevorzuge hier, sofern die Umstände nicht dagegen sprechen, die starke Typisierung. Der Grund ist schlicht der, dass so weitestgehend sichergestellt ist, dass keine unerwarteten oder gar fehlerhaften Konvertierungen vom System vorgenommen werden – sofern ich dem nicht explizit zustimme, wozu die meisten Programmiersprachen entsprechende Typumwandlungen vorsehen. Auch hier ist jedoch zugegebenermaßen üblicherweise ein etwas höherer Aufwand notwendig, als bei eher schwächer typisierten Programmiersprachen.

Meine persönliche Präferenz statischer, expliziter und starker Typsysteme ist tatsächlich auch als eben solche zu verstehen. Ich erkenne durchaus den Vorteil eher "dynamischerer" Sprachen an und nutze diesen ebenfalls in einigen Projekten. Ich habe jedoch für mich festgestellt, mit o. g. Verhalten produktiver zu arbeiten und halte mich daher soweit sinnvoll an solche Sprachen. Insbesondere in C# – was wohl kaum eine Überraschung sein dürfte – finde ich hier eine sehr gute Kombination dieser Aspekte umgesetzt. Wem bisher nur die eine Seite geläufig ist, empfehle ich auf jeden Fall, sich auch einmal eine Sprache mit anderem Typsystem genauer anzusehen.

Insgesamt ist dies eine sehr grobe Einteilung, von der viele Programmiersprachen eine etwas differenziertere vornehmen und tlw. auch Kombinationen erlauben. Dennoch eignet sich dies durchaus für einen ersten Überblick der Möglichkeiten; einige Aspekte hängen auch bis zu einem gewissen Grad voneinander ab. Interessierte sollten sich definitiv beispielsweise einmal vom entsprechenden Wikipedia-Artikel ausgehend weiter informieren.

Quelltext des .NET-Frameworks veröffentlicht 👍 👎

Einige werden es bereits erfahren haben, dennoch möchte auch ich darauf hinweisen, dass Microsoft weitere große Teile des .NET-Frameworks öffentlich zur Verfügung stellt. Unterstützt wird das Vorhaben durch Roslyn.

Die Inhalte sind dabei durchgängig interaktiv gestaltet und stehen unter der Reference Source License.

Hashing in C# und PHP 👍 👎

Den meisten PHP-Entwicklern sind die beiden Funktionen md5(…) und sha1(…) geläufig. Diese liefern zu einer übergebenen Zeichenkette einen Streuwert ("Hash") in hexadezimaler Darstellung. Zu diesem Zweck gibt es in C# die beiden Klassen MD5CryptoServiceProvider und SHA1CryptoServiceProvider.

Diesen Klassen kann man über die ComputeHash-Methode unter anderem ein byte-Array übergeben und bekommt daraufhin ein solches zurück, welches den Hash darstellt. Da wir es in der Praxis aber doch meist einfach nur mit Zeichenketten und u. U. auch mit verschiedenen Hash-Algorithmen (z. B. MD5, SHA1) zu tun haben, möchten wir das Ganze in eine komfortable generische Erweiterungsmethode für Zeichenketten verpacken:
Methode implementieren
0102030405060708
public static string GetHash<T>(this string str) where T : HashAlgorithm, new() {    using(T hashProvider = new T()) {          // Streuwert berechnen        byte[] hash = hashProvider.ComputeHash(Encoding.UTF8.GetBytes(str));
return Encoding.UTF8.GetString(hash); }}
Für diejenigen, denen die Arbeit mit generischen Typen (noch) nicht geläufig ist, werde ich in Kürze auch noch einen etwas ausführlicheren Beitrag zu diesem Thema verfassen.

Die Rückgabe dieser Methode dürfte nun jedoch leider noch gar nicht dem entsprechen, was viele erwarten – die hexadezimale Schreibweise wie in PHP, so dass diese Ausgaben insbesondere nicht kompatibel zueinander für Vergleiche sind, was die Interoperabilität natürlich massiv beeinträchtigt. C# liefert standardmäßig nämlich das, was bei den entsprechenden PHP-Funktionen mit dem Parameter $raw_output erhältlich ist.

Da die hexadezimale Darstellung jedoch tatsächlich häufig praktikabler ist, stützen wir uns ausnahmsweise ( Smiley: winking) auf diese Vorgehensweise und bringen C# zu diesem – unter anderem von PHP gewohnten – Verhalten:
Methode implementieren (hexadezimale Rückgabe)
0102030405060708
public static string GetHash<T>(this string str) where T : HashAlgorithm, new() {    using(T hashProvider = new T()) {          // Streuwert berechnen        byte[] hash = hashProvider.ComputeHash(Encoding.UTF8.GetBytes(str));
return BitConverter.ToString(hash).Replace("-", String.Empty).ToLower(); }}
Jetzt liefert unsere Erweiterungsmethode für C# exakt das gleiche Ergebnis wie PHP das standardmäßig macht und kann darüber hinaus besonders einfach und flexibel auf jeder Zeichenkette verwendet werden:
Methode verwenden
01
string hashTest = "test".GetHash<SHA1CryptoServiceProvider>();
Durch Übergabe der gewünschten Implementierung als Typparameter erhalten wir nun direkt das Ergebnis.

Alphabetischer Index mit C# und LINQ 👍 👎

Häufig kann es sinnvoll sein, eine Liste nach Anfangsbuchstaben gruppiert darzustellen. Dies kann unter C# in Verbindung mit LINQ sehr einfach und elegant geschehen, wozu wir wie üblich ein kleines Beispiel durchgehen.

Konkret möchten wir Personen nach Anfangsbuchstabe von LastName gruppiert und darin wiederum nach LastName sortiert ausgeben. Auf Grund der Trivialität der Klasse verweise ich auf das im Artikel zu Eigenschaften in C# präsentierte Beispiel und fahre direkt mit der Liste fort:
Liste von Personen
01020304050607080910111213141516171819202122
List<Person> personList = new List<Person>() {    new Person() {        FirstName = "Harald",        LastName = "Müller"    },    new Person() {        FirstName = "Helga",        LastName = "Schmidt"    },    new Person() {        FirstName = "Karl",        LastName = "Schneider"    },    new Person() {        FirstName = "Olaf",        LastName = "Fischer"    },    new Person() {        FirstName = "Saskia",        LastName = "Weber"    }};
Nun führen wir die Gruppierung ganz einfach per GroupBy(…) aus und speichern dies mit den dazugehörigen Listen per ToDictionary(…) direkt in ein Dictionary:
Liste gruppiert abspeichern
010203
Dictionary<char, List<Person>> personStructure = personList    .GroupBy(p => p.LastName[0])  // Gruppierung nach erstem Buchstaben des Nachnamens    .ToDictionary(k => k.Key, v => v.ToList());
Anschließend können wir die Struktur beispielsweise durchlaufen und auf einer Webpräsenz ausgeben:
Struktur durchlaufen
01020304050607080910
foreach(KeyValuePair<char, List<Person>> data in personStructure) {    // "data.Key" enthält den Anfangsbuchstaben
foreach(Person person in data.Value.OrderBy(p => p.LastName)) {
/** * "person" enthält nun jeweils eine zu eben * diesem Anfangsbuchstaben passende Person. **/ }}
Das Ergebnis einer solchen Vorgehensweise lässt sich u. a. in der neuen Ansicht der Bezirkszuordnungen auf meinem Städteportal für Berlin und Hamburg betrachten. Für diese einfache Darstellung kommt übrigens noch ein zusätzlicher kleiner LINQ-"Trick" mit Select(…) zum Einsatz. Für unser Beispiel könnten wir dazu die innere Schleife schlicht durch Folgendes ersetzen und damit Vor- und Nachname per Komma separiert ausgeben:
Einfache Zeichenketten-Konkatenierung
01
String.Join(", ", data.Value.Select(p => p.FirstName + " " + p.LastName));

Datenfelder in C# und PHP 👍 👎

Da ich regelmäßig – insbesondere von PHP-Entwicklern – gefragt werde, wie sich das dort allseits beliebte Array in C# verwenden lässt, möchte ich dazu eine kleine Gegenüberstellung präsentieren.

Zuerst einmal sollte festgestellt werden, dass Arrays unter PHP sehr vielseitig eingesetzt werden, wofür andere Programmiersprachen (wie beispielsweise C#) jeweils eigens optimierte Datenstrukturen vorsehen. Nicht zu vergessen ist natürlich ganz allgemein die unter C# strengere Typisierung.

Wir beginnen die Beispiele demnach also erst einmal mit einfachen Listen und gehen schließlich auch auf mehrdimensionale und assoziative Felder ein.
Liste von Zeichenketten: PHP
010203
$data = ["Ab", "Cd", "Ef"];
$data[] = "Yz";
Liste von Zeichenketten: C# (nativ)
01
string[] data = {"Ab", "Cd", "Ef"};
Es gibt jedoch noch eine elegantere Lösung: Generische Container. Hierbei handelt es sich um vom konkreten Datentyp abstrahierte Mechanismen zur Sammlung und Verwaltung von Daten. Sehen wir uns also das o. g. Beispiel mit Hilfe der generischen List-Klasse in C# an:
Liste von Zeichenketten: C# (generisch)
010203
List<string> data = new List<string>() {"Ab", "Cd", "Ef"};
data.Add("Yz");
Durch den Aufruf entsprechender Instanzmethoden können wir dadurch sehr komfortabel neue Daten hinzufügen, den Inhalt sortieren und natürlich auch per LINQ darauf operieren. Dank Implementierung eines sog. Indexers ist im Übrigen auch bei dieser Variante der gewohnte null-basierte Zugriff per Index z. B. per data[0] möglich.

Gehen wir nun also einen Schritt weiter und stellen mehrdimensionale Felder gegenüber:
Mehrdimensionales Feld: PHP
010203040506
$data = [    ["Ab", "Cd"],    ["Ef", "Gh"]];
$data[] = ["Wx", "Yz"];
Mehrdimensionales Feld: C# (nativ)
01020304
string[,] data = {    {"Ab", "Cd"},    {"Ef", "Gh"}};
Mehrdimensionales Feld: C# (generisch)
010203040506
List<List<string>> data = new List<List<string>>() {    new List<string>() {"Ab", "Cd"},    new List<string>() {"Ef", "Gh"}};
data.Add(new List<string> {"Wx", "Yz"});
Zum Schluss sollen natürlich noch assoziative Felder erwähnt werden, welche wir in C# mit Hilfe eines Dictionary umsetzen:
Assoziatives Feld: PHP
010203040506
$data = [    'FirstName' => "Holger",    'LastName' => "Stehle"];
$data['City'] = "Berlin";
Assoziatives Feld: C#
0102030405060708
Dictionary<string,string> data = new Dictionary<string,string>() {    {"FirstName", "Holger"},    {"LastName", "Stehle"}};
data.Add("City", "Berlin"); // – oder auch -data["City"] = "Berlin";
Zum Durchlaufen eines solchen Feldes in C# bietet sich die Verwendung der KeyValuePair-Struktur an:
Assoziatives Feld durchlaufen: C#
01020304
foreach(KeyValuePair<string,string> entry in data) {    // entry.Key: "FirstName", "LastName" und "City"    // entry.Value: "Holger", "Stehle" und "Berlin"}
Das soll es für den Moment auch erst einmal gewesen sein, zukünftig möchte ich jedoch noch ein paar derartiger Gegenüberstellungen durchführen. Insbesondere auch auf die generische Entwicklung unter C# werde ich noch gesondert in weiteren Beiträgen eingehen.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche