DateTimeOffset (Ketat)

Pagi ini temanku kirillkos menemui masalah.


Kode masalah


Ini kodenya:


class Event { public string Message {get;set;} public DateTime EventTime {get;set;} } interface IEventProvider { IEnumerable<Event> GetEvents(); } 

Dan kemudian banyak, banyak implementasi dari IEventProvider yang IEventProvider data dari berbagai tabel dan basis data.


Masalah : di semua pangkalan ini, semuanya ada di zona waktu yang berbeda. Karena itu, ketika mencoba untuk menampilkan acara ke UI, semuanya sangat membingungkan.


Kemuliaan bagi Halesberg, kami memiliki tipe, semoga mereka menyelamatkan kami!


Percobaan 1


 class Event { public string Message {get;set;} public DateTimeOffset EventTime {get;set; } } 

DateTimeOffset tipe yang hebat, ia menyimpan informasi offset tentang UTC. Ini sangat didukung oleh MS SQL dan Entity Framework (dan dalam versi 6.3 itu akan didukung lebih baik ). Dalam gaya kode kami, itu wajib untuk semua kode baru.


Sekarang kita dapat mengumpulkan informasi dari penyedia yang sama dan secara konsisten, bergantung pada jenisnya, menampilkan semuanya di UI. Kemenangan!


Masalah : DateTimeOffset dapat secara implisit mengkonversi dari DateTime .
Kode berikut ini mengkompilasi dengan baik:


 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!"}, }; } 

Ini karena DateTimeOffset operator pengecoran implisit yang ditentukan:


 // Local and Unspecified are both treated as Local public static implicit operator DateTimeOffset (DateTime dateTime); 

Ini sama sekali bukan yang kita butuhkan. Kami hanya ingin programmer untuk berpikir, ketika menulis kode: "Tapi di zona waktu apa peristiwa ini terjadi?" Dari mana mendapatkan zona itu? " Seringkali dari bidang yang sama sekali berbeda, kadang-kadang dari tabel terkait. Dan kemudian melakukan kesalahan tanpa berpikir sangat mudah.


Konversi tersirat yang terkutuk!


Percobaan 2


Karena saya dengar palu analisa statis , semuanya menurut saya kuku kasus yang cocok untuk mereka. Kita perlu menulis analisa statis yang melarang konversi implisit ini, dan menjelaskan mengapa ... Sepertinya banyak pekerjaan. Bagaimanapun, itu adalah tugas kompiler untuk memeriksa jenis. Untuk saat ini, cantumkan ide ini sebagai kata kerja.


Percobaan 3


Sekarang, jika kita akan berada di dunia F #, kata Kirillkos .
Kami kemudian akan:


 type DateTimeOffsetStrict = Value of DateTimeOffset 

Dan selanjutnya tidak datang dengan improvisasi semacam sihir akan menyelamatkan kita. Sayang sekali mereka tidak menulis F # di kantor kami, dan kami juga tidak benar-benar mengenal Kirillkos :-)


Percobaan 4


Tidak bisakah sesuatu dilakukan di C #? Itu mungkin, tetapi Anda tersiksa dengan mengubah bolak-balik. Berhenti, tetapi kami baru saja melihat bagaimana Anda dapat melakukan konversi implisit!


 /// <summary> /// Same as <see cref="DateTimeOffset"/> /// but w/o implicit conversion from <see cref="DateTime"/> /// </summary> public readonly struct DateTimeOffsetStrict { private DateTimeOffset Internal { get; } private DateTimeOffsetStrict(DateTimeOffset @internal) { Internal = @internal; } public static implicit operator DateTimeOffsetStrict(DateTimeOffset dto) => new DateTimeOffsetStrict(dto); public static implicit operator DateTimeOffset(DateTimeOffsetStrict strict) => strict.Internal; } 

Hal yang paling menarik tentang tipe ini adalah bahwa itu secara implisit dikonversi bolak-balik dari DateTimeOffset , tetapi upaya untuk secara implisit mengubahnya dari DateTime akan menyebabkan kesalahan kompilasi, konversi dari DateTime hanya mungkin secara eksplisit. Kompiler tidak dapat menyebut "rantai" konversi implisit, jika mereka didefinisikan dalam kode kami, standar melarangnya ( kutipan dari SO ). Yaitu, kerjanya seperti ini:


 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!"}, }; } 

tapi tidak seperti ini:


 IEnumerable<Event> GetEvents() { return new[] { new Event() {EventTime = DateTime.Now, Message = "Hello from unknown time!"}, }; } 

Apa yang kami butuhkan!


Ringkasan


Kami belum tahu apakah kami akan mengimplementasikannya. Hanya semua orang yang terbiasa dengan DateTimeOffset, dan sekarang menggantinya dengan tipe kami bodoh. Ya, dan yang pasti masalah akan muncul di tingkat pengikatan parameter EF, ASP.NET, dan di ribuan tempat. Tetapi solusinya sendiri nampak menarik bagi saya. Saya menggunakan trik serupa untuk memantau keamanan input pengguna - Saya membuat jenis UnsafeHtml , yang secara implisit dikonversi dari string, tetapi Anda dapat mengubahnya kembali menjadi string atau IHtmlString hanya dengan memanggil pembersih.

Source: https://habr.com/ru/post/id438946/


All Articles