Heute morgen ist mein Freund Kirillkos auf ein Problem gestoßen .
Problemcode
Hier ist sein Code:
class Event { public string Message {get;set;} public DateTime EventTime {get;set;} } interface IEventProvider { IEnumerable<Event> GetEvents(); }
Und dann viele, viele Implementierungen von IEventProvider
, die Daten aus verschiedenen Tabellen und Datenbanken IEventProvider
.
Problem : In all diesen Basen befindet sich alles in verschiedenen Zeitzonen. Dementsprechend ist beim Versuch, Ereignisse auf der Benutzeroberfläche auszugeben, alles furchtbar verwirrt.
Ehre sei Halesberg, wir haben Typen, mögen sie uns retten!
Versuch 1
class Event { public string Message {get;set;} public DateTimeOffset EventTime {get;set; } }
DateTimeOffset
großartiger Typ, der Offset-Informationen zu UTC speichert. Es wird perfekt von MS SQL und Entity Framework unterstützt (und in Version 6.3 wird es noch besser unterstützt ). In unserem Codestil ist dies für jeden neuen Code obligatorisch.
Jetzt können wir Informationen von denselben Anbietern sammeln und unter Verwendung von Typen konsistent alles auf der Benutzeroberfläche anzeigen. Sieg!
Problem : DateTimeOffset
kann implizit von DateTime
konvertieren.
Der folgende Code wird gut kompiliert:
class Event { public string Message {get;set;} public DateTimeOffset EventTime {get;set; } } IEnumerable<Event> GetEvents() { return new[] { new Event() {EventTime = DateTime.Now, Message = "Hello from unknown time!"}, }; }
Dies liegt daran, dass für DateTimeOffset
ein impliziter Casting-Operator definiert ist:
Das brauchen wir überhaupt nicht. Wir wollten nur, dass der Programmierer beim Schreiben des Codes denkt: "Aber in welcher eigenen Zeitzone ist dieses Ereignis passiert?" Woher bekommt man die Zone? " Oft aus ganz anderen Bereichen, manchmal aus verwandten Tabellen. Und dann einen Fehler zu machen, ohne sehr leicht nachzudenken .
Verdammt implizite Konvertierungen!
Versuch 2
Da habe ich davon gehört ein Hammer statische Analysatoren , alles scheint mir Nägel geeignete Fälle für sie. Wir müssen einen statischen Analysator schreiben, der diese implizite Konvertierung verbietet und erklärt, warum ... Es sieht nach viel Arbeit aus. Auf jeden Fall ist es Aufgabe des Compilers, die Typen zu überprüfen. Setzen Sie diese Idee vorerst als ausführlich ein.
Versuch 3
Nun, wenn wir in der Welt von F # wären, sagte Kirillkos .
Wir würden dann:
type DateTimeOffsetStrict = Value of DateTimeOffset
Und weiter kam nicht mit Improvisation Eine Art Magie würde uns retten. Schade, dass sie in unserem Büro kein F # schreiben und wir Kirillkos auch nicht wirklich kennen :-)
Versuch 4
Kann in C # nichts getan werden? Es ist möglich, aber Sie werden gequält, indem Sie hin und her konvertieren. Hören Sie auf, aber wir haben gerade gesehen, wie Sie implizite Conversions durchführen können!
Das Interessanteste an diesem Typ ist, dass er implizit von DateTimeOffset
hin und her DateTimeOffset
wird. Ein Versuch, ihn implizit von DateTime
zu konvertieren, DateTime
jedoch zu einem Kompilierungsfehler. Konvertierungen von DateTime sind nur explizit möglich. Der Compiler kann die "Kette" impliziter Konvertierungen nicht aufrufen. Wenn sie in unserem Code definiert sind, ist dies durch den Standard verboten ( Zitat aus SO ). Das heißt, es funktioniert so:
class Event { public string Message {get;set;} public DateTimeOffsetStrict EventTime {get;set; } } IEnumerable<Event> GetEvents() { return new[] { new Event() {EventTime = DateTimeOffset.Now, Message = "Hello from unknown time!"}, }; }
aber nicht so:
IEnumerable<Event> GetEvents() { return new[] { new Event() {EventTime = DateTime.Now, Message = "Hello from unknown time!"}, }; }
Was wir brauchten!
Zusammenfassung
Wir wissen noch nicht, ob wir es umsetzen werden. Nur jeder war an DateTimeOffset gewöhnt, und jetzt ist es dumm, es durch unseren Typ zu ersetzen. Ja, und auf der Ebene der EF-, ASP.NET-Parameterbindung und an tausend Stellen treten mit Sicherheit Probleme auf. Aber die Lösung selbst scheint mir interessant zu sein. Ich habe ähnliche Tricks verwendet, um die Sicherheit von Benutzereingaben zu überwachen. Ich habe den Typ UnsafeHtml
, der implizit aus einer Zeichenfolge konvertiert wird. Sie können ihn jedoch nur durch Aufrufen von IHtmlString
in eine Zeichenfolge oder einen IHtmlString
.