DateTimeOffset(严格)

今天早上,我的朋友基里科斯遇到了一个问题。


问题代码


这是他的代码:


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

然后, IEventProvider许多实现从不同的表和数据库IEventProvider数据。


问题 :在所有这些基准中,一切都在不同的时区中。 因此,当尝试将事件输出到UI时,一切都变得非常混乱。


荣耀归于Halesberg,我们有各种类型,愿他们拯救我们!


尝试1


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

DateTimeOffset很好的类型;它存储有关UTC的偏移量信息。 MS SQL和Entity Framework完全支持它(并且在6.3版中将更好地支持它)。 在我们的代码样式中,所有新代码都是必需的。


现在,我们可以从这些相同的提供程序中收集信息,并始终依靠类型来在UI上显示所有内容。 胜利了!


问题DateTimeOffset可以从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#的世界。
然后,我们将:


 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 ,该类型是字符串隐式转换 ,但是您只能通过调用sanitizer将其转换回字符串或IHtmlString

Source: https://habr.com/ru/post/zh-CN438946/


All Articles