是否有技术原因没有从DBNull到可空类型的隐式转换?

时间:2012-01-10 01:51:18

标签: c# .net vb.net

是否有技术原因导致没有从DBNull到各种可空和/或sql类型的隐式转换?我理解为什么转换当前没有发生,但是不明白为什么当时没有创建隐式转换或者在框架的后续版本中添加了隐式转换。

为了清楚起见,我正在寻找技术原因,而不是“因为他们是这样做的”或“我喜欢它”。

2 个答案:

答案 0 :(得分:7)

最直接的解释来自Microsoft's documentation

  

CLR可空类型不用于存储数据库空值,因为ANSI SQL null的行为与空引用(或Visual Basic中的Nothing)的行为不同。

答案 1 :(得分:3)

好吧,我不知道SqlTypes的情况,但肯定有一些技术原因可以解释为什么在DBNull.ValueNullable<T>的值之间添加隐式转换{{1}不行。

请注意,HasValue = false是一种引用类型,尽管DBNull 行为就像引用类型一样 - 假装能够承担{{} 1}} value - 它实际上是一个值类型,带有值语义。

特别是,当Nullable<T>类型的值被加框时,会出现一种奇怪的边缘情况。该行为在运行时特殊情况下为null类型的框值,为Nullable<T>的盒装版本,而不是Nullable<T>的盒装版本。

正如the MSDN documentation解释的那样:

  

当装入可空类型时,公共语言运行库会自动选中Nullable(Of T)对象的基础值,而不是Nullable(Of T)对象本身。也就是说,如果HasValue属性为true,则Value属性的内容将被加框。当可空类型的基础值被取消装箱时,公共语言运行库会创建一个初始化为基础值的新Nullable(Of T)结构。

     

如果可空类型的HasValue属性为false,则装箱操作的结果为Nothing。因此,如果将盒装的可空类型传递给期望对象参数的方法,则必须准备该方法以处理参数为Nothing的情况。当Nothing被取消装入可以为空的类型时,公共语言运行库会创建一个新的Nullable(Of T)结构并将其HasValue属性初始化为false。

现在我们遇到了一个棘手的问题:C#语言规范(§4.3.2)说我们无法使用拆箱转换将T转换为Nullable<T>

  

对于在给定的 nullable-type 的取消装箱转换在运行时成功,源操作数的值必须为null或对底层的盒装值的引用可空类型的非可空值类型。如果源操作数是对不兼容对象的引用,则抛出DBNull.Value

根据§10.10.3,我们也无法使用用户定义的转换从Nullable<T>转换为System.InvalidCastException

  

无法直接重新定义预定义的转化。因此,转换运算符不允许从object转换为Nullable<T>,因为object与所有其他类型之间已存在隐式和显式转换。

好的,你或我无法做到,但微软可以修改规范,并使其合法,对吧?我不这么认为。

为什么呢?好吧,想象一下预期的用例:你有一些指定返回object的方法。实际上,它会返回objectDBNull.Value。但编译器怎么知道呢?它只知道该方法被指定为返回int。并且必须在编译时选择要应用的转换运算符。

好的,假设有一些魔术运算符可以从object转换为object,编译器可以通过某种方式知道它何时适用。 (我们不希望它用于指定返回Nullable<T>的每个方法 - 如果方法实际返回object该怎么办?)但是我们还有一个问题:转换可能含糊不清!如果该方法返回stringlong,我们会DBNull.Value,那么当该方法返回一个方框int? v = Method();时我们应该怎么做?

基本上,为了使其按预期工作,您必须使用等效的long来确定运行时的类型并根据运行时类型进行转换。但后来我们又打破了另一条规则:由于实际转换只能在运行时选择,因此无法保证它实际上会成功。但隐式转换不应该抛出异常。

所以在这一点上,它不仅是对语言的指定行为的改变,一个潜在的重大性能打击,而且它可能会引发意外的异常!这似乎是不实施它的一个很好的理由。但如果您还需要一个理由,请记住每个功能都以minus 100 points开头。

简而言之:无论如何,你真正想要的是隐式转换。如果您想要dynamic的行为,请使用dynamic!这样做你想要的,并已在C#4.0中实现:

dynamic
相关问题