DateTimeOffset (صارم)

هذا الصباح واجه صديقي kirillkos مشكلة.


رمز المشكلة


هنا هو رمزه:


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

ثم العديد من تطبيقات IEventProvider التي تقوم IEventProvider البيانات من جداول وقواعد بيانات مختلفة.


المشكلة : في كل هذه القواعد ، كل شيء في مناطق زمنية مختلفة. وفقًا لذلك ، عند محاولة إخراج الأحداث إلى واجهة المستخدم ، يكون كل شيء مشوشًا بشكل رهيب.


المجد لهلسبرغ ، لدينا أنواع ، فهل ينقذنا!


محاولة 1


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

DateTimeOffset رائعًا ؛ فهو يخزن معلومات الإزاحة حول UTC. إنه مدعوم تمامًا بواسطة MS SQL و Entity Framework (وفي الإصدار 6.3 ، سيتم دعمه بشكل أفضل ). في نمط الشفرة الخاص بنا ، يعد إلزاميًا لجميع الرموز الجديدة.


الآن يمكننا جمع المعلومات من نفس الموفرين ونعمل دائمًا على الاعتماد على الأنواع وعرض كل شيء على واجهة المستخدم. النصر!


المشكلة : يمكن تحويل DateTime ضمنيًا من DateTime .
التعليمات البرمجية التالية بتصنيف جيد:


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

وذلك لأن DateTimeOffset على عامل تشغيل ضمني محدد:


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

هذا ليس على الإطلاق ما نحتاجه. أردنا فقط أن يفكر المبرمج ، عند كتابة التعليمات البرمجية: "لكن في أي منطقة زمنية خاصة حدث هذا الحدث؟" من أين تحصل على المنطقة؟ غالبًا من حقول مختلفة تمامًا ، وأحيانًا من الجداول ذات الصلة. ثم لارتكاب خطأ دون التفكير بسهولة.


ملعون التحويلات الضمنية!


محاولة 2


منذ سمعت عنه مطرقة محللون ثابت ، كل شيء يبدو لي الأظافر الحالات المناسبة لهم. نحتاج إلى كتابة محلل ثابت يحظر هذا التحويل الضمني ، ويشرح لماذا ... يبدو أنه كثير من العمل. على أي حال ، فإن مهمة المترجم هي التحقق من الأنواع. الآن ، ضع هذه الفكرة في شكل مطوّل.


محاولة 3


الآن ، إذا كنا في عالم F # ، قال kirillkos .
نحن ثم:


 type DateTimeOffsetStrict = Value of DateTimeOffset 

و كذلك لم يأت بالارتجال سوف ينقذنا نوع من السحر. إنه لأمر مؤسف أنهم لا يكتبون F # في مكتبنا ، ونحن لا نعرف Kirillkos حقًا إما :-)


محاولة 4


لا يمكن القيام بشيء ما في C #؟ هذا ممكن ، لكنك تعذبت عن طريق التحويل ذهابًا وإيابًا. توقف ، ولكن رأينا للتو كيف يمكنك إجراء تحويلات ضمنية!


 /// <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; } 

الشيء الأكثر إثارة للاهتمام حول هذا النوع هو أنه يتم تحويله ضمنيًا وإيابًا من DateTimeOffset ، لكن محاولة تحويله ضمنيًا من DateTime ستتسبب في حدوث خطأ في الترجمة ، التحويلات من DateTime ممكنة فقط بشكل صريح. لا يمكن للمترجم استدعاء "سلسلة" التحويلات الضمنية ، إذا تم تعريفها في التعليمات البرمجية الخاصة بنا ، فإن المعيار يحظرها ( اقتباس من 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!"}, }; } 

ولكن ليس مثل هذا:


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

ما نحتاجه!


ملخص


لا نعرف حتى الآن ما إذا كنا سنطبقها. لقد اعتاد الجميع فقط على DateTimeOffset ، والآن استبداله بنوعنا غبي. نعم ، وبالتأكيد ستظهر مشكلات على مستوى EF ، وربط معلمة ASP.NET وفي آلاف الأماكن. لكن الحل نفسه يبدو مثيرا للاهتمام بالنسبة لي. لقد استخدمت حيلًا مماثلة لمراقبة أمان إدخال المستخدم - قمت بإجراء النوع UnsafeHtml ، والذي يتم تحويله ضمنيًا من سلسلة ، ولكن يمكنك تحويله مرة أخرى إلى سلسلة أو IHtmlString فقط عن طريق الاتصال IHtmlString .

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


All Articles