小心VB.NET中的除运算符"/"和"/"
VB.NET中除运算符有两种,普通除"/"和整数除"/",如果我们写程序时不注意两者的区别,很容易造成潜在的错误,这种错误很隐蔽,不容易被发现。而且VB.NET中类型转换和C#差别很大,应该引起我们足够的重视,这些看似微不足道的细节却直接关系都我们代码的健壮性。
1.问题的引出
下面是开发中遇到问题代码的简化部分,输入大部分数据都没问题,但当输入数字为18时会抛出异常“System.ArgumentException: 偏移量和长度超出数组的界限,于从索引到源集合结尾处的元素数量。在 System.Collections.ArrayList.GetRange(Int32 index,Int32 count)”。是什么原因使ArrayList集合越界呢?这和VB.NET中的除运算符有什么关系呢?当我们理解了VB.NET中两种除的区别以及类型转换(Double—>Integer)的实质后,问题的答案也就不言自明了。
引出问题的代码
SubMain() Console.WriteLine("请输入一个整数:") F1(Console.ReadLine()) Console.ReadLine() EndSub
PublicSubF1(ByValtimesAsInteger) DimlistAsArrayList=NewArrayList() '填充数据 ForiAsInteger=0To29 list.Add(i) Next '把集合list中元素分成times份进行处理 DimoneTimeNumAsInteger=list.Count/times ForiAsInteger=0Totimes-1 DimlengthAsInteger=oneTimeNum Ifi=times-1Then '最后一次循环取集合中所有剩余数据 length=list.Count-oneTimeNum*i EndIf '实际中这里是启动线程处理,这里简化只是来说明问题 F2(list.GetRange(oneTimeNum*i,length)) Next EndSub
PrivateSubF2(ByValalAsArrayList) '对ArrayList集合al进行处理 EndSub
2.普通除"/"和整数除"/"
1)普通除:expression1 / expression2
结果是 expression1 除以 expression2 的完整的商,包括任何余数。执行除法之前,任何整数数值表达式(除数和被除数)都会被扩展为 Double。如果将结果赋给整数数据类型,Visual Basic 会试图将结果从 Double 转换成这种类型。
举例说明:30 / 18 = 1.6666666666666667,执行除法前被除数30和除数18都扩展为Double类型,结果也为Double类型。
2)整数除:expression1/ expression2
结果是 expression1 除以 expression2 的整数商,它丢弃了所有余数,只保留整数部分(称为截断)。结果数据类型是数值类型,对应于 expression1 和 expression2 的数据类型。值得注意的一点,如果除数或被除数为浮点数,在执行除法前,编译器会采用“四舍六入五成双”的规则将其转换成Long类型,再执行除法。
举例说明:30 / 18 = 1,只保留结果的整数部分。
3.VB.NET中的类型转换(Double—>Integer)
根据第二部分对普通除的解释,当CLR执行Dim oneTimeNum As Integer =30 / 18时,首先将被除数30和除数18都扩展为Double类型,进行除运算得到Double类型的结果1.6666666666666667,因为要将Double类型数据赋值给Integer类型变量,此时要执行强制类型转换,得到最终结果oneTimeNum = 2(可能很多人和我一样会奇怪结果为什么不是1)。我们从IL代码中查看VB.NET中从Double—>Integer类型转换的实质。
函数F1对应的IL代码
.methodpublicstaticvoidF1(int32times)cilmanaged { //代码大小123(0x7b) .maxstack3 .localsinit([0]class[mscorlib]System.Collections.ArrayListlist, [1]int32oneTimeNum, [2]int32i, [3]int32V_3, [4]int32length, [5]int32VB$t_i4$L0, [6]int32VB$CG$t_i4$S0, [7]boolVB$CG$t_bool$S0) IL_0000:nop IL_0001:newobjinstancevoid[mscorlib]System.Collections.ArrayList::.ctor() IL_0006:stloc.0 IL_0007:ldc.i4.0 IL_0008:stloc.2 IL_0009:ldloc.0 IL_000a:ldloc.2 IL_000b:box[mscorlib]System.Int32 IL_0010:callvirtinstanceint32[mscorlib]System.Collections.ArrayList::Add(object) IL_0015:pop IL_0016:nop IL_0017:ldloc.2 IL_0018:ldc.i4.1 IL_0019:add.ovf IL_001a:stloc.2 IL_001b:ldloc.2 IL_001c:ldc.i4.s29 IL_001e:stloc.sVB$CG$t_i4$S0 IL_0020:ldloc.sVB$CG$t_i4$S0 IL_0022:ble.sIL_0009 IL_0024:ldloc.0 IL_0025:callvirtinstanceint32[mscorlib]System.Collections.ArrayList::get_Count() IL_002a:conv.r8 IL_002b:ldarg.0 IL_002c:conv.r8 IL_002d:div IL_002e:callfloat64[mscorlib]System.Math::Round(float64)//重点看这句 IL_0033:conv.ovf.i4 IL_0034:stloc.1 IL_0035:ldc.i4.0 IL_0036:ldarg.0 IL_0037:ldc.i4.1 IL_0038:sub.ovf IL_0039:stloc.sVB$t_i4$L0 IL_003b:stloc.3 IL_003c:br.sIL_0070 IL_003e:ldloc.1 IL_003f:stloc.slength IL_0041:ldloc.3 IL_0042:ldarg.0 IL_0043:ldc.i4.1 IL_0044:sub.ovf IL_0045:ceq IL_0047:stloc.sVB$CG$t_bool$S0 IL_0049:ldloc.sVB$CG$t_bool$S0 IL_004b:brfalse.sIL_0059 IL_004d:ldloc.0 IL_004e:callvirtinstanceint32[mscorlib]System.Collections.ArrayList::get_Count() IL_0053:ldloc.1 IL_0054:ldloc.3 IL_0055:mul.ovf IL_0056:sub.ovf IL_0057:stloc.slength IL_0059:nop IL_005a:ldloc.0 IL_005b:ldloc.1 IL_005c:ldloc.3 IL_005d:mul.ovf IL_005e:ldloc.slength IL_0060:callvirtinstanceclass[mscorlib]System.Collections.ArrayList[mscorlib]System.Collections.ArrayList::GetRange(int32, int32) IL_0065:callvoidVBTest.Module1::F2(class[mscorlib]System.Collections.ArrayList) IL_006a:nop IL_006b:nop IL_006c:ldloc.3 IL_006d:ldc.i4.1 IL_006e:add.ovf IL_006f:stloc.3 IL_0070:ldloc.3 IL_0071:ldloc.sVB$t_i4$L0 IL_0073:stloc.sVB$CG$t_i4$S0 IL_0075:ldloc.sVB$CG$t_i4$S0 IL_0077:ble.sIL_003e IL_0079:nop IL_007a:ret }//endofmethodModule1::F1
从IL代码可以看出,VB.NET中执行类型转换实际上是调用的函数[mscorlib]System.Math::Round(float64),MSDN中对这个函数的解释:将双精度浮点值舍入为最接近的整数,如果参数为两个整数的中值,这两个整数一个为偶数,另一个为奇数,则返回偶数(也就是我们常说的“四舍六入五成双”)。 现在,可以很好的解释文章开始提出的问题了:由于输入18时,oneTimeNum的值为2,当循环到第16次时i = 15,此时执行list.GetRange(oneTimeNum * i,length)即list.GetRange(30,2),已经超出了list的长度范围,所以会抛出异常。
4.C#和VB.NET的区别
1)C#中的除运算"/"符相当于VB.NET的整数除"/"运算符; 2)C#中从Double—>Integer类型转换必须要采用显示方式,且转换规则为直接舍弃小数位。
总结这次出现问题的根源是用C#语法去推断VB.NET语法,因为接触C#较早,而C#和VB.NET语法又大同小异,忽略了对VB.NET基本语法的学习,以后应多注意两种语言的差别,尽量减少类似的错误。还有一点需要注意,遇到问题的时候多查MSDN,似乎现在都习惯从网上寻求答案,但网上关于 VB.NET除运算符的内容并不多,找了半天,才发现MSDN上写的很详细,我想查找资料的顺序应该是:MSDN—>CNBlogs找找看— >Google/Baidu。
Tag标签:
C#,VB,运算符,类型转换
原文:http://www.cnblogs.com/freshman0216/archive/2008/08/27/1276991.html (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|