为什么“指定的强制转换无效”错误没有显示根本原因?

15

刚刚发现了一个简单的错误,这引发了一个有趣的问题。环境:VS 2010,.NET 4,C#。从 SQL 存储过程中获取返回值时出现了“指定的转换无效”异常:

cm.Parameters.Add( "@si", SqlDbType.SmallInt ).Direction=   ParameterDirection.ReturnValue;
..
cm.ExecuteNonQuery( );
short   siRetVal=   (short) cm.Parameters[0].Value;     // exception here

由于代码在调试模式下运行并在那一行中断,我在即时窗口中输入了以下内容:

?(short) cm.Parameters[0].Value

结果是:

Cannot unbox 'cm.Parameters[0].Value' as a 'short'

这个SQL存储过程返回一个32位的 int (不是我最初以为的16位的 short),这解释了异常。应该为这个参数使用正确的宽度-毫无疑问。

但是有人能解释一下为什么只有在立即窗口中才报告错误的根本原因吗?在异常助手中没有详细信息公开,内部异常为空。知道确切的原因不是很有益吗?我认为这会使错误分析更加简单,不是吗?


编辑: 添加了屏幕截图;堆栈跟踪似乎没有暗示任何ADO.NET代码(我没有期望)。更有甚者,如果我将返回值参数“声明”为SmallInt,但提供一个适当宽度的变量(或像所示的转换),容纳一个int,则一切正常!我相当肯定这与ADO/SQL无关。

我相信Hans Passant揭示了这里发生的真实情况,并倾向于接受他的解答。尽管发现托管代码甚至不能为我提供有关执行状态的准确详细信息(例如,哪个参考实际上为空-如上所述或在这种情况下无效转换)有些令人失望。


1
谁知道该向谁提出此事以便将其包含在即将发布的.NET更新中? - David
1
这是一个伟大的谜团,我们不应该试图去理解它。沉浸在宁静中,接受你无法改变的事实。 - Roy Dictus
2
我理解你的痛苦。在一次特别糟糕的经历后,我发布了这篇文章:http://programmers.stackexchange.com/questions/37475/what-are-developers-problems-with-helpful-error-messages - Moo-Juice
5
另外,空引用异常,是哪个引用出了问题?!我知道你知道!! - Erix
1
差异的存在是因为调试器不是编译器。异常信息不够详细的原因是JIT需要生成非常高效和紧凑的代码。请参阅https://dev59.com/f3I-5IYBdhLWcg3w-9wK#3076525。 - Hans Passant
显示剩余10条评论
1个回答

0

如果你分析ADO.NET生成的SQL,你可能会发现ADO.NET试图将输出参数显式定义为smallint类型。因此,当SQL Server执行你的请求时,它试图将存储过程返回的值(一个int)分配到一个由你告诉它创建为tinyint的变量中。我相信你看到的转换异常来自于SQL Server,而不是.NET。

在命令窗口中,你使用的是CLR,而不是TSQL查询引擎。你的抱怨可能需要针对查询引擎,而不是.NET。


2
抱怨并不是关于SQL-vs-ADO.NET或值的'宽度'与变量来容纳它的错误,而是关于代码可用的精确信息的缺乏。由于即时窗口确实暴露了根本原因,所以“它可以在异常中首先包含”。在许多情况下,这将极大地帮助弄清楚出了什么问题,最重要的是为什么。当然,我们学习了诸如不在同一行上使用多个复杂表达式之类的技巧-从而通过行号来定位异常-以确定薄弱环节。但在我看来,这对于.NET来说是一个无聊的借口。 - Astrogator
一个好的建议,我会重新设置场景并添加堆栈跟踪以查看它从哪里冒出来(上次没保存)。 - Astrogator
所以我认为我的建议是,SQL Server抛出了“指定的转换无效错误”,而ADO.NET只是捕获了该错误。 SQL Server可能没有提供足够详细的深度,以使您的应用程序引发更有意义的异常。在CLR中独占执行的操作实际上是类型感知的。顺便说一句,您可以考虑使用隐式类型变量并将分配包装在.TryParse()调用中。 - syneptody
不,这个问题与SQL/ADO无关,请在ImmediateWindow中尝试:?(short)((object)77777) - 结果是Cannot unbox '((object)(77777))' as a 'short'! 事实上,它甚至无法执行?(short)((object)0)这主要与该值比给定占位符更宽有关。 另外,SQL客户端库是使用本地代码编写的,而不是.NET(否则我会非常失望:)。 - Astrogator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接