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

jmo*_*eno 6 .net c# vb.net

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

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

com*_*ech 7

最简单的解释来自Microsoft的文档:

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

  • 换句话说,我们故意没有这样做,因为我们希望您完全了解自己在做什么. (5认同)

Dan*_*den 3

好吧,我不知道这种SqlTypes情况,但肯定有一些技术原因导致在withDBNull.Value和 值之间添加隐式转换不起作用。Nullable<T>HasValue = false

请记住,DBNull是一种引用类型,尽管它的Nullable<T> 行为类似于引用类型(通过假装能够接受null值),但它实际上是一种具有值语义的值类型。

特别是,当类型值Nullable<T>被装箱时,会出现奇怪的边缘情况。该行为在运行时是特殊情况,用于将类型的值装箱Nullable<T>为 的装箱版本T,而不是 的装箱版本Nullable<T>

正如MSDN 文档所解释的:

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

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

现在我们遇到了一个棘手的问题:C# 语言规范 (§4.3.2) 规定我们不能使用拆箱转换来转换DBNull.ValueNullable<T>

为了在运行时成功进行到给定nullable-type 的拆箱转换,源操作数的值必须为 null 或对nullable -type 的基础非 nullable-value-type的装箱值的引用。如果源操作数是对不兼容对象的引用,则抛出 a 。System.InvalidCastException

根据§10.10.3,我们也不能使用用户定义的转换来从 转换为objectNullable<T>

无法直接重新定义预定义的转换。因此,不允许转换运算符进行相互转换,object因为object所有其他类型之间已经存在隐式和显式转换。

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

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

好的,假设有一些神奇的运算符可以从 转换objectNullable<T>,并且编译器有某种方式知道它何时适用。(我们不希望它用于指定返回的每个object方法- 如果该方法实际上返回 a ,它应该做什么string?)但是我们仍然有一个问题:转换可能不明确!如果该方法返回long, 或DBNull.Value, 并且我们这样做了int? v = Method();,那么当该方法返回一个装箱的时我们应该做什么long

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

因此,此时,这不仅是对语言指定行为的更改、潜在的重大性能影响,而且最重要的是,它可能会引发意外的异常!这似乎是不实施它的一个很好的理由。但如果您还需要一个理由,请记住,每项功能的起始分数都是负 100 分

简而言之:无论如何,您真正想要的东西都无法通过隐式转换来完成。如果您想要 的行为dynamic,只需使用dynamic! 这可以满足您的要求,并且已经在 C# 4.0 中实现:

object x = 23;
object y = null;

dynamic dx = x;
dynamic dy = y;

int? nx = (int?) dx;
int? ny = (int?) dy;

Console.WriteLine("nx.HasValue = {0}; nx.Value = {1}; ny.HasValue = {2};", 
    nx.HasValue, nx.Value, ny.HasValue);
Run Code Online (Sandbox Code Playgroud)