UNIX-Zeitstempel und C# 👍 👎

In einem älteren Beitrag zum Thema Erweiterungsmethoden in C# hatte ich eine kleine Methode zur Arbeit mit UNIX-Zeitstempeln als Beispiel verwendet. Dieser Beitrag wird auch heute noch relativ häufig aufgerufen, weswegen ich zu diesem konkreten Beispiel anmerken möchte, dass das .NET-Framework seit Version 4.6 Funktionalität zur Arbeit mit entsprechenden Zeitstempeln auf der DateTimeOffset-Struktur bereithält:
Die Verwendung gestaltet sich wie üblich sehr einfach:
Konvertierung von UNIX-Zeitstempeln
010203040506070809
  // UNIX-Zeitstempel für aktuelles Datum und aktuelle Uhrzeit ermittelnlong unixTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
// bestimmtes Datum um 0:00 Uhr verwendenunixTimestamp = (new DateTimeOffset(new DateTime(1988, 1, 29))).ToUnixTimeSeconds();

// DateTime-Objekt lokaler Zeit aus UNIX-Zeitstempel erzeugenDateTime dateTime = DateTimeOffset.FromUnixTimeSeconds(unixTimestamp).LocalDateTime;
Eigene Implementierungen sollten für die aktuelle Praxis daher nicht mehr nötig sein.

Aufzählungswert per Zeichenkette ermitteln 👍 👎

Vor sehr langer Zeit habe ich bereits einen Artikel zu Enumerationen in C# geschrieben, an welchen ich indirekt anknüpfen möchte. Oftmals steht man im Bereich der Softwareentwicklung vor dem Problem der Interoperabilität, da mehrere – bisweilen äußerst unterschiedliche – Systeme zusammenarbeiten müssen.

Ein klassischer Fall im Bereich der Webentwicklung ist die Zusammenarbeit aus Anwendung und Datenbank, beispielsweise hier im Blog zwischen der Programmiersprache C# mit ASP.NET MVC-Framework und MySQL-Datenbank. Etwas konkreter soll es darum gehen, C#-Aufzählungstypen zusammen mit MySQL-ENUMs zu verwenden, welche grundsätzlich erst einmal als bloße Zeichenketten zu behandeln sind. Für das folgende Beispiel nehmen wir an, dass eine Tabelle user existiert, die eine Spalte state vom Typ ENUM('ACTIVE','INACTIVE','LOCKED') enthält.

Unter Verwendung des ADO.NET Entity Framework würde beim Database First-Ansatz eine entsprechende Klasse user mit der Eigenschaft state vom Typ string erzeugt. Da es sich um eine partielle Klasse handelt, erweitern wir diese um eine neue Eigenschaft State. Diese soll anschließend die automatische Konvertierung für state übernehmen und ermöglicht uns im Programmcode einen typsicheren Zugriff auf den Status des Benutzers:
Aufzählungstyp UserState anlegen
0102030405
public enum UserState {    Active,    Inactive,    Locked}
Erweiterung der Klasse user
010203040506070809
public partial class user {    public UserState State {        get {            return (UserState) Enum.Parse(typeof(UserState), this.state, true);        }        set {            this.state = value.ToString().ToUpper();        }    }
Die Verwendung gestaltet sich nun – mit beiden Eigenschaften – wie gewohnt komfortabel:
Verwendung
0102030405060708091011121314151617
user dbUser = new user() {    State = UserState.Active};  // üblicherweise aus Datenbank ermittelt

string state = dbUser.state; // "ACTIVE"UserState userState = dbUser.State; // Active
dbUser.State = UserState.Inactive;

state = dbUser.state; // "INACTIVE"userState = dbUser.State; // Inactive
dbUser.state = "LOCKED";

state = dbUser.state; // "LOCKED"userState = dbUser.State; // Locked
Weitere Informationen zu den Enum.Parse-Methoden finden sich wie üblich im MSDN. Es lohnt sich außerdem ein Blick auf die Enum.TryParse(…)-Methoden mit generischem Typ-Parameter.

DNS-Blacklisten mit C# abfragen 👍 👎

Im früheren Beitrag zur DNS-Auflösung mit C# haben wir eine grundlegende Möglichkeit kennengelernt, IP-Adressen zu Domains zu ermitteln. Dies möchten wir uns zu Nutze machen, um DNS-Blacklisten abzufragen, welche von vielen eMail-Systemen genutzt werden, um potentiellen Spam zu erkennen (und ggf. abzuweisen).

Zu den bekannten Anbietern entsprechender Dienste gehören beispielsweise: Diese setze ich auch selbst ein und habe jeweils gute Erfahrungen damit gemacht. Bitte informiert euch jedoch vor einem Einsatz beim jeweiligen Anbieter über die genauen Konditionen, der kommerzielle Einsatz ist beispielsweise oftmals kostenpflichtig. Ebenso verhält es sich je nach Anbieter ab einem gewissen Abfragevolumen.

Um nun eine IP-Adresse gegen die entsprechenden Dienste prüfen zu können, müssen wir diese dem oben angegebenen Hostnamen in umgekehrter Reihenfolge voranstellen und eine Namensauflösung durchführen.

Konkret fragen wir also beispielsweise bei einer Prüfung der IP-Adresse 80.78.81.19 auf Spam-Aktivität laut Spamhaus das Ergebnis der Domain 19.81.78.80.zen.spamhaus.org ab (welches hoffentlich negativ beschieden werden sollte). Per C# könnte man dies nun beispielsweise (durchaus verbesserungsfähig) wie folgt durchführen:
IP-Adresse auf Blacklisting prüfen
010203040506070809101112131415161718192021222324
public static bool CheckBlacklisting(IPAddress ipAddress, string blacklist = "zen.spamhaus.org") {    string ipAddressReversed = String.Join(".", ipAddress.GetAddressBytes().Reverse());    string hostName = String.Concat(ipAddressReversed, ".", blacklist);
try { foreach(IPAddress hostAddress in Dns.GetHostAddresses(hostName)) { /** * Achtung: Diese Implementierung ist naiv und sollte nicht direkt * übernommen werden. Je nach Anbieter, so beispielsweise auch bei * Spamhaus, gilt nicht der gesamte Loopback-Adressbereich als Spam. * Daher bitte vorher beim Anbieter informieren und konkretisieren! **/ if(IPAddress.IsLoopback(hostAddress)) { return true; } }
return false; } catch { return false; }}
bool checkResult = CheckBlacklisting(IPAddress.Parse("80.78.81.19")); // false
Das Ergebnis der Namensauflösung, also die erhaltene IP-Adresse, gibt darüber hinaus Aufschluss über den genaueren Status. Spamhaus liefert dazu beispielsweise in ihren FAQ weiterführende Informationen.

DNS-Auflösung mit C# 👍 👎

Das Domain Name System ist einer der wichtigsten Dienste in vielen IP-basierten Netzwerken, so insbesondere auch im Internet. Die Hauptaufgabe stellt dabei das Übersetzen von für die menschliche Nutzung praktischen Domains in technisch verarbeitbare IP-Adressen dar. Mit diesem Beitrag möchte ich jedoch gar nicht weiter auf diese Details eingehen, dazu lohnt sich stattdessen beispielsweise ein (empfehlenswerter) Blick in die Wikipedia.

Unser Hauptanliegen soll es nun sein, dieses System per C# zu befragen, um darauf aufbauend in einem späteren Beitrag eine konkrete Anwendung umzusetzen. Praktischerweise liefert das .NET-Framework mit System.Net.Dns eine Klasse mit entsprechender Funktionalität – zugegebenermaßen aber nur mit geringem Funktionsumfang, weshalb zur professionellen Arbeit mit den verschiedenen Resource Records das Einbinden externer Programmbibliotheken sinnvoll sein kann. Die Anwendung der Basisfunktionalität ist jedoch denkbar einfach:
IP-Adresse(n) zu Domain ermitteln
0102030405060708
IPAddress[] hostAddressList = Dns.GetHostAddresses("coders-online.net");
foreach(IPAddress hostAddress in hostAddressList) { /** * hostAddress enthält nun jeweils eine IP-Adresse zur Domain; * im Falle dieses Beispiels den einzigen Eintrag 80.78.81.19. **/}
Eine praktische Hilfsmethode ist außerdem Dns.GetHostName(), welche den Namen des lokalen Rechners liefert. Das MSDN liefert wie üblich weitere Informationen zur Dns-Klasse, u. a. mit Hinweisen zu den asynchronen Methoden. Weiterhin interessant im System.Net-Namespace ist bis dahin natürlich auch die Klasse IPAddress.

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.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche