MSDN博客中的一篇文章提到了.NET 3.5 SP1会带来的处个修正,见以下代码:
var floats = new ArrayList { 2.5f, 3.5f, 4.5f };
var ints = from int i in floats
select i;
注意from后面声明的显式类型(int)。开发者会很自然地认为ints为[2,3,4],但在现在版本.NET 3.5环境下得到的却是[2,4,4]。
原来那段LINQ表达式将会被编译为以下方法调用:
var ints = floats.Cast<int>().Select<int,int>(i => i);
问题便出现在Cast这个扩展方法中,而Cast内部是通过调用Convert类的ToInt32方法转换ArrayList中元素类型的,而这个方法既不是对浮点数进行截去小数,也不是简单的round: ToInt32方法的文档中是这样描述返回值的:
value rounded to the nearest 32-bit signed integer. If value is halfway between two whole numbers, the even number is returned; that is, 4.5 is converted to 4, and 5.5 is converted to 6.
这种数值转换的方式被称为Banker's rounding。
这是个程序语义的问题,将在以后的.NET Frameworkk 3.5 SP1中解决,估计只是修改Cast方法的实现。现在的应对方法是在一些应用场景中不使用显式范围变量。