هذا الصباح واجه صديقي 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
على عامل تشغيل ضمني محدد:
هذا ليس على الإطلاق ما نحتاجه. أردنا فقط أن يفكر المبرمج ، عند كتابة التعليمات البرمجية: "لكن في أي منطقة زمنية خاصة حدث هذا الحدث؟" من أين تحصل على المنطقة؟ غالبًا من حقول مختلفة تمامًا ، وأحيانًا من الجداول ذات الصلة. ثم لارتكاب خطأ دون التفكير بسهولة.
ملعون التحويلات الضمنية!
محاولة 2
منذ سمعت عنه مطرقة محللون ثابت ، كل شيء يبدو لي الأظافر الحالات المناسبة لهم. نحتاج إلى كتابة محلل ثابت يحظر هذا التحويل الضمني ، ويشرح لماذا ... يبدو أنه كثير من العمل. على أي حال ، فإن مهمة المترجم هي التحقق من الأنواع. الآن ، ضع هذه الفكرة في شكل مطوّل.
محاولة 3
الآن ، إذا كنا في عالم F # ، قال kirillkos .
نحن ثم:
type DateTimeOffsetStrict = Value of DateTimeOffset
و كذلك لم يأت بالارتجال سوف ينقذنا نوع من السحر. إنه لأمر مؤسف أنهم لا يكتبون F # في مكتبنا ، ونحن لا نعرف Kirillkos حقًا إما :-)
محاولة 4
لا يمكن القيام بشيء ما في C #؟ هذا ممكن ، لكنك تعذبت عن طريق التحويل ذهابًا وإيابًا. توقف ، ولكن رأينا للتو كيف يمكنك إجراء تحويلات ضمنية!
الشيء الأكثر إثارة للاهتمام حول هذا النوع هو أنه يتم تحويله ضمنيًا وإيابًا من 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
.