小心VB.NET中的除运算符"/"和"\"
2008-08-27 00:06
489 查看
VB.NET中除运算符有两种,普通除"/"和整数除"\",如果我们写程序时不注意两者的区别,很容易造成潜在的错误,这种错误很隐蔽,不容易被发现。而且VB.NET中类型转换和C#差别很大,应该引起我们足够的重视,这些看似微不足道的细节却直接关系都我们代码的健壮性。
1.问题的引出
下面是开发中遇到问题代码的简化部分,输入大部分数据都没问题,但当输入数字为18时会抛出异常“System.ArgumentException: 偏移量和长度超出数组的界限,于从索引到源集合结尾处的元素数量。在 System.Collections.ArrayList.GetRange(Int32 index, Int32 count)”。是什么原因使ArrayList集合越界呢?这和VB.NET中的除运算符有什么关系呢?当我们理解了VB.NET中两种除的区别以及类型转换(Double—>Integer)的实质后,问题的答案也就不言自明了。
函数F1对应的IL代码
.method public static void F1(int32 times) cil managed
{
// 代码大小 123 (0x7b)
.maxstack 3
.locals init ([0] class [mscorlib]System.Collections.ArrayList list,
[1] int32 oneTimeNum,
[2] int32 i,
[3] int32 V_3,
[4] int32 length,
[5] int32 VB$t_i4$L0,
[6] int32 VB$CG$t_i4$S0,
[7] bool VB$CG$t_bool$S0)
IL_0000: nop
IL_0001: newobj instance void [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: callvirt instance int32 [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.s 29
IL_001e: stloc.s VB$CG$t_i4$S0
IL_0020: ldloc.s VB$CG$t_i4$S0
IL_0022: ble.s IL_0009
IL_0024: ldloc.0
IL_0025: callvirt instance int32 [mscorlib]System.Collections.ArrayList::get_Count()
IL_002a: conv.r8
IL_002b: ldarg.0
IL_002c: conv.r8
IL_002d: div
IL_002e: call float64 [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.s VB$t_i4$L0
IL_003b: stloc.3
IL_003c: br.s IL_0070
IL_003e: ldloc.1
IL_003f: stloc.s length
IL_0041: ldloc.3
IL_0042: ldarg.0
IL_0043: ldc.i4.1
IL_0044: sub.ovf
IL_0045: ceq
IL_0047: stloc.s VB$CG$t_bool$S0
IL_0049: ldloc.s VB$CG$t_bool$S0
IL_004b: brfalse.s IL_0059
IL_004d: ldloc.0
IL_004e: callvirt instance int32 [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.s length
IL_0059: nop
IL_005a: ldloc.0
IL_005b: ldloc.1
IL_005c: ldloc.3
IL_005d: mul.ovf
IL_005e: ldloc.s length
IL_0060: callvirt instance class [mscorlib]System.Collections.ArrayList [mscorlib]System.Collections.ArrayList::GetRange(int32,
int32)
IL_0065: call void VBTest.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.s VB$t_i4$L0
IL_0073: stloc.s VB$CG$t_i4$S0
IL_0075: ldloc.s VB$CG$t_i4$S0
IL_0077: ble.s IL_003e
IL_0079: nop
IL_007a: ret
} // end of method Module1::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。
1.问题的引出
下面是开发中遇到问题代码的简化部分,输入大部分数据都没问题,但当输入数字为18时会抛出异常“System.ArgumentException: 偏移量和长度超出数组的界限,于从索引到源集合结尾处的元素数量。在 System.Collections.ArrayList.GetRange(Int32 index, Int32 count)”。是什么原因使ArrayList集合越界呢?这和VB.NET中的除运算符有什么关系呢?当我们理解了VB.NET中两种除的区别以及类型转换(Double—>Integer)的实质后,问题的答案也就不言自明了。
函数F1对应的IL代码
.method public static void F1(int32 times) cil managed
{
// 代码大小 123 (0x7b)
.maxstack 3
.locals init ([0] class [mscorlib]System.Collections.ArrayList list,
[1] int32 oneTimeNum,
[2] int32 i,
[3] int32 V_3,
[4] int32 length,
[5] int32 VB$t_i4$L0,
[6] int32 VB$CG$t_i4$S0,
[7] bool VB$CG$t_bool$S0)
IL_0000: nop
IL_0001: newobj instance void [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: callvirt instance int32 [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.s 29
IL_001e: stloc.s VB$CG$t_i4$S0
IL_0020: ldloc.s VB$CG$t_i4$S0
IL_0022: ble.s IL_0009
IL_0024: ldloc.0
IL_0025: callvirt instance int32 [mscorlib]System.Collections.ArrayList::get_Count()
IL_002a: conv.r8
IL_002b: ldarg.0
IL_002c: conv.r8
IL_002d: div
IL_002e: call float64 [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.s VB$t_i4$L0
IL_003b: stloc.3
IL_003c: br.s IL_0070
IL_003e: ldloc.1
IL_003f: stloc.s length
IL_0041: ldloc.3
IL_0042: ldarg.0
IL_0043: ldc.i4.1
IL_0044: sub.ovf
IL_0045: ceq
IL_0047: stloc.s VB$CG$t_bool$S0
IL_0049: ldloc.s VB$CG$t_bool$S0
IL_004b: brfalse.s IL_0059
IL_004d: ldloc.0
IL_004e: callvirt instance int32 [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.s length
IL_0059: nop
IL_005a: ldloc.0
IL_005b: ldloc.1
IL_005c: ldloc.3
IL_005d: mul.ovf
IL_005e: ldloc.s length
IL_0060: callvirt instance class [mscorlib]System.Collections.ArrayList [mscorlib]System.Collections.ArrayList::GetRange(int32,
int32)
IL_0065: call void VBTest.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.s VB$t_i4$L0
IL_0073: stloc.s VB$CG$t_i4$S0
IL_0075: ldloc.s VB$CG$t_i4$S0
IL_0077: ble.s IL_003e
IL_0079: nop
IL_007a: ret
} // end of method Module1::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。
相关文章推荐
- 小心VB.NET中的除运算符"/"和"/"
- 小心VB.NET中的除运算符"/"和"\"
- 从VB.Net到VB6.0要小心,关于使用IIF和log求对数函数(串联的小知识)
- [史上最全]C#(VB.NET)中位运算符工作过程剖析(译)
- vb.net 与 c# 运算符区别
- VB.NET中的除法运算符 与 C#中的除法运算符
- vb.net 教程 1-5 运算符 1
- C#中的除法运算符与VB.NET中的除法运算符
- [VB.Net] 运算符
- VB.NET类型转换相关运算符应用技巧分享
- VB.NET的示例使用 AddressOf 运算符来指定线程的启动函数(多线程)
- vb.net 教程 1-5 运算符 2
- 利用两种VB.NET串联运算符方法讲解
- VB.Net - 运算符
- vb.net 教程 4-2 目录操作 DirectoryInfo 4
- [VB.NET][新手求助]vb.net怎么生成exe文件
- [VB.NET]VS2005控件工具栏消失了
- VB.NET操作SQL Server完全模块
- [VB.NET]转word问题
- [VB.NET]如何在应用程序中保存密码?在线等