今天早上,我的朋友基里科斯遇到了一个问题。
问题代码
这是他的代码:
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
定义的隐式转换运算符:
这根本不是我们所需要的。 我们只是希望程序员在编写代码时思考:“但是此事件在什么时区发生?” 从哪里获得区域?” 通常来自完全不同的字段,有时来自相关表。 然后犯错而又不容易思考 。
该死的隐式转换!
尝试2
自从我听说 锤子 静态分析仪 ,一切在我看来 指甲 适合他们的情况。 我们需要编写一个静态分析器来禁止这种隐式转换,并说明原因……这看起来像做很多工作。 无论如何,检查类型是编译器的工作。 现在,把这个想法说得很详细。
尝试3
基里尔科斯说,现在,如果我们要进入F#的世界。
然后,我们将:
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
,该类型是从字符串隐式转换的 ,但是您只能通过调用sanitizer将其转换回字符串或IHtmlString
。