小心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)的实质后,问题的答案也就不言自明了。

引出问题的代码
    Sub Main()
        Console.WriteLine("请输入一个整数:")
        F1(Console.ReadLine())
        Console.ReadLine()
    End Sub

Public Sub F1(ByVal times As Integer)
        Dim list As ArrayList = New ArrayList()
        '填充数据
        For i As Integer = 0 To 29
            list.Add(i)
        Next
        '把集合list中元素分成times份进行处理
        Dim oneTimeNum As Integer = list.Count / times
        For i As Integer = 0 To times - 1
            Dim length As Integer = oneTimeNum
            If i = times - 1 Then
                '最后一次循环取集合中所有剩余数据
                length = list.Count - oneTimeNum * i
            End If
            '实际中这里是启动线程处理,这里简化只是来说明问题
            F2(list.GetRange(oneTimeNum * i, length))
        Next
    End Sub

Private Sub F2(ByVal al As ArrayList)
        '对ArrayList集合al进行处理
    End Sub

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代码
.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。

Tag标签: C#,VB,运算符,类型转换

原文:http://www.cnblogs.com/freshman0216/archive/2008/08/27/1276991.html

小心VB.NET中的除运算符/和/相关推荐

  1. linux shell 三元运算符,语法 - Bash中的三元运算符(?:)

    语法 - Bash中的三元运算符(?:) 有没有办法做这样的事情 int a = (b == 5) ? c : d; 用Bash? 14个解决方案 346 votes ternary operator ...

  2. C++中重载下标运算符[]

    1.C++中重载下标运算符[] C++规定,下标运算符[]必须以成员函数的形式进行重载,该重载函数在类中的声明格式如下: 返回值类型 & operator[] (参数列表);// 或者cons ...

  3. python中的成员运算符用于判断什么_Python之运算符

    原标题:Python之运算符 这章我们介绍如何用Python的运算符,大家不懂的地方可以加群:579817333咨询学习 Python运算符包括赋值运算符.算术运算符.关系运算符.逻辑运算符.位运算符 ...

  4. java比较字符引用地址_java中的比较运算符== 与 equals()方法

    初学JAVA的时候对这两种比较方式也有疑惑,趁着现在整理一下自己的理解. JAVA中变量分为两类,一类是值类型,它储存的是变量真正的值,比如基础数据类型,值类型储存在内存的栈中:一类是引用类型,他们储 ...

  5. JavaScript中的instanceof运算符是什么?

    本文翻译自:What is the instanceof operator in JavaScript? The instanceof keyword in JavaScript can be qui ...

  6. VB.Net中关于数组赋值

    在VB.Net中增加了一个修饰Shared.Shared 关键字指示一个或多个被声明的编程元素将被共享.要点是:共享元素不关联于某类或结构的特定实例.需要通过使用类名或结构名称或者类或结构的特定实例的 ...

  7. 如何在VB例程中接收自定义消息

    代码 如何在VB例程中接收自定义消息  您可以用API函数SetWindowLong指定处理消息的窗口过程(window procedure)为自定义的函数WindowProc,捕获消息ID为WM_U ...

  8. C语言中的位运算符主要有哪些?逻辑右移与算术右移的区别?

    逻辑右移与算术右移的区别? 逻辑右移就是不考虑符号位,右移一位,左边补零即可. 算术右移需要考虑符号位,右移一位,若符号位为1,就在左边补1,:否则,就补0. 所以算术右移也可以进行有符号位的除法,右 ...

  9. C#中的{n}运算符

    今天学到了C#中的{n}运算符,感觉这个运算符就是对C++中%d运算符的一种改进, 因为{n}运算符能够指定替换列表中的第几个,就比%d之类的运算符操作起来省力很多. 我的例程: 1 using Sy ...

最新文章

  1. java.net.inetaddress_java.net.InetAddress类的应用
  2. [STL] UVA 10815 安迪的第一个字典 Andy's First Dictionary
  3. 滴答定时器的计数模式_【高手私藏】STM32学习笔记:SysTick滴答时钟
  4. 这是一个无效的源路径/url
  5. Oracle分析函数Over()
  6. 如何在Bash脚本中将Heredoc写入文件?
  7. Sutherland-Hodgeman多边形裁剪
  8. 1.4.2.PHP5.6 狐教程-环境(Mac下 PHP开发环境 配置及安装 php5.6.x nginx mysql)
  9. 如何升级更新你的黑莓手机OS系统
  10. STM32 串口程序下载
  11. gwas snp 和_GWAS | 原理和流程 | 全基因组关联分析
  12. 常用邮箱哪家好用?TOM邮箱口碑评价
  13. 百度地图的反地址解析(通过经纬度查询地址信息)
  14. centos7.9安装zabbix+添加局域网下其他客户机
  15. MOS管当开关控制时,一般用PMOS做上管NMOS做下管的原因
  16. 强大的实用的mac软件卸载应用软件,彻底清除App残留
  17. Python——下载数据集时报错解决:ContentTooShortError: <urlopen error retrieval incomplete: got only XX out of XX
  18. Java实现上传(支持多个文件同时上传)和下载
  19. 真实生活的记录:我三年的外企生涯(1)出处:天涯虚拟社区
  20. 圆瓶、扁瓶、三色瓶砖、数百品牌分选,弓叶科技的分选神技

热门文章

  1. javascript:void(0);用法及常见问题解析
  2. 解读Junit的@Test注解,避免initializationerror
  3. 一天搞定CSS(扩展):CSS Hack
  4. 杭电oj1176,2084java实现
  5. 简单生产消费模式的代码流程(Java代码)
  6. [转]linux 有效用户和实际用户的区别
  7. 教你从0到1搭建秒杀系统-防超卖
  8. 解决SQL注入与XSS攻击
  9. Android自定义sleep图,android自定义view实现钟表效果
  10. php判断表单修改内容,JavaScript判断用户是否对表单进行了修改的方法_javascript技巧...