C# · 12月 23, 2021

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

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

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

解决方法 好吧,我不知道sqlTypes的情况,但肯定有一些技术原因,为什么在DBNull.Value和Nullable< T>的值之间添加隐式转换.使用HasValue = false将无法正常工作.

记住,DBNull是一种引用类型,尽管Nullable< T>像一个引用类型 – 假装能够采用空值 – 它实际上是一个值类型,具有值语义.

特别是,当类型为Nullable< T>的值时,存在一种奇怪的边缘情况.盒装.该行为在运行时特殊情况下为Nullable< T>类型的框值.到盒装版本的T,而不是Nullable< T>的盒装版本.

正如the MSDN documentation解释的那样:

When a nullable type is @R_489_2419@ed,the common language runtime automatically @R_489_2419@es the underlying value of the Nullable(Of T) object,not the Nullable(Of T) object itself. That is,if the HasValue property is true,the contents of the Value property is @R_489_2419@ed. When the underlying value of a nullable type is un@R_489_2419@ed,the common language runtime creates a new Nullable(Of T) structure initialized to the underlying value.

If the HasValue property of a nullable type is false,the result of a @R_489_2419@ing operation is Nothing. Consequently,if a @R_489_2419@ed nullable type is passed to a method that expects an object argument,that method must be prepared to handle the case where the argument is Nothing. When Nothing is un@R_489_2419@ed into a nullable type,the common language runtime creates a new Nullable(Of T) structure and initializes its HasValue property to false.

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

For an un@R_489_2419@ing conversion to a given nullable-type to succeed at run-time,the value of the source operand must be either null or a reference to a @R_489_2419@ed value of the underlying non-nullable-value-type of the nullable-type. If the source operand is a reference to an incompatible object,a system.invalidCastException is thrown.

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

It is not possible to directly redefine a pre-defined conversion. Thus,conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types.

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

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

好吧,假设有一些神奇的运算符可以从对象转换为Nullable< T>,并且编译器有一些方法可以知道它何时适用. (我们不希望它用于指定返回对象的每个方法 – 如果方法实际返回一个字符串,它应该怎么做?)但是我们仍然有一个问题:转换可能不明确!如果方法返回long或DBNull.Value,我们做int? v = Method();,当方法返回一个盒装长的时候我们应该怎么做?

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

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

简而言之:无论如何,你真正想要的是隐式转换.如果你想要动态的行为,只需使用动态!这样做你想要的,并已在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);