收录助教君在VB习题课上最常被问到的问题,每周更新,希望对大家有所帮助。
Q1.如何让新的文本内容接在原来的内容后面/下一行显示?
A1.例如,Label1.text原本的内容是"VB程序设计",现在要把"助教好帅"接到原来的内容后面,应该这样写
还有缩写形式:
Label1.text &= "助教好帅"
与第一种写法等价。
要换行显示怎么办呢?拼接上换行符vbCrLf就行了,换行符表示“在此处开始新的一行”。如:
Label1.text &= "助教好帅" & vbCrLf
Q2.(针对第四周作业)如何累计加班天数?
A2. 首先要给大家讲一点基础知识,叫做“变量的作用域”。什么意思呢?每个Dim出来的变量,都是有自己的“活动范围”的,出了这个范围,这个变量就不存在了。
举个例子:
也就是说,如果你在一个Sub内部(也就是Sub到End Sub这两行之间的区域)定义的变量,出了这个Sub就无法访问了。如果你在Button2_Click这个Sub里也定义了一个变量a,那这个a和Button1_Click里那个a没有丝毫关系。打个比方,比如你单名一个“飞”字,但你姓唐,那以“姓”为作用域来划分的话,你就和《三国演义》里那个同样名叫“飞”的人区分开了,因为他姓张。
来看下面这个问题:我想定义一个整型变量sum,初始值为0,每点击一次Button1时sum就加1,点击Button2则在Label1里显示Button1被点击了几次(也就是sum的值)。这怎么做到呢?
显然,sum不能被Dim在任何一个Sub里,因为两个Sub都要用到。
解决方法是,在“通用声明段”里声明sum。
全局变量”,而把Sub内部定义的变量(如前面的a)称为“局部变量”。
常见的错误:
这样做的后果是什么?答案是,Sub里声明的局部变量会“屏蔽”同名的全局变量,按这种写法,在Button2_Click这个Sub里,能访问到的sum只能是Sub内部定义的那个sum,而不是全局的sum。
Q3. 我写的这些代码,到底什么时候、按什么顺序运行?是一点那个三角型按钮就依次运行吗?
A3. 这里又要给大家介绍一个概念,叫做“事件驱动”,什么意思呢?简单地说就是“有事就处理,没事就歇着”。VB就是典型的事件驱动型编程。
我们写的代码,并不是在程序一开始运行时就被执行,而是要“等待时机”。在VB里,每个控件都有很多系统预定义好的事件,比如一次鼠标点击(click)、按一次键盘(keypress)、窗口加载完成(onload)、文本内容发生改变(textchange)等等。系统会监听这些事件,当这些事件发生时,系统会执行相应的代码来响应事件的发生。好比上课老师点名让学生回答问题,点到谁的名字,谁就起来回答,没被点到的人坚决不吭声,这就叫“点名驱动型课堂”。
所以我们写代码的时候,其实是按照以下步骤来做的:
1.选择要处理哪个控件上发生的事件,是button1上的?还是textbox1上的?
2.选择要处理的事件类型,是点了下鼠标?还是按了下键盘?
3.当做完选择之后,系统会自动给我们初始化一段代码,就是该事件触发时要执行的那个代码段的头和尾(Sub 和 End Sub),我们只需要在这个代码段内部写好我们的代码,每次当这个事件被触发时,这段代码就被执行一次。
当光标落在某个sub范围内时,上面的两个栏会自动显示这个代码段对应的是哪个控件的哪个事件。
所以大家注意,你写的代码当且仅当相应的事件发生时才会被执行。而且,相应的事件每发生一次,这段代码就执行一次。
反例:下面这段代码不work,因为每一次KeyPress时,这段代码都会从count=0开始原原本本地执行一遍。
要正确的初始化count值,应该在声明时直接初始化:Dim count As Integer = 0。
最后,系统对一些控件的常用功能做了快捷设置,比如在设计界面,双击一个按钮,系统会自动把光标定位到处理click事件的代码段内。双击Textbox、Label又会如何呢?自己试下吧!
Q4.数组的声明方法总结
在VB中声明数组,只有两种方法,而且都必须符合“先声明后使用”的原则:
(1)如果声明时知道自己的数组长度,比如10,那就直接在声明里写出来,例如:Dim a(9) As Integer
(2)如果声明时不知道数组的长度,那就先声明一个最大下标为空的数组,然后在使用前必须用ReDim语句为它确定长度,例如:
Dim a(),b As Integer
b = Val(TextBox1.text) ‘数组长度从文本框获得
ReDim
... '使用a数组
非要问为什么,那是因为a()这样的声明形式并没有为数组真正分配内存空间(因为程序也不知道该分配多少给数组),所以必须用ReDim为数组分配内存空间,然后才能使用它。
误:Dim n,a(n) As Integer 这样其实等效于Dim n,a(0) As Integer,因为在这个地方的n只是刚刚被声明还没有初始值。而且声明里的n是“一次性”的,以后n的值无论怎么变化,a的大小都不会跟着变。
Q5.彻底弄懂子过程/函数的概念
子过程/函数其实就是执行一个特定功能的一段代码,大家这学期其实已经用过很多函数了,比如UBound,Mid,Val,Math.abs等等。下面分几块来讲(因为子过程是VB特有的概念,在别的语言中一般只有函数,所以下文如无特殊说明,我就以“函数”指代VB中的函数和子过程。
(1)形式参数(形参)和实际参数(实参)之辨
这恐怕是对于初学函数的同学来说最挠头的问题了。来看一段代码
Private Function func(ByVal a As Integer,ByVal b As Integer)
Dim s As Integer
s = (a + b)/( a * b )
If( s> 10) Then
Return 10
Else
Return s
End If
End Function
这是一个简单的函数。
然后我们来调用这个函数:
Dim v1,v2,v3,v4,s1,s2 As Integer
...
v1 = 10
v2 = 15
...
s1 = func(v1,v2)
...
s2 = func(v3,v4)
...
【前方高能!】才华横溢的猪脚将上面的定义及调用过程拟人化讲给你们听:
VB公司成立了新的部门(函数)func,以下是公司CEO老V和即将上任的部门经理小B的聊天记录。
老V:小B,你知道你部的工作任务吗?
小B:当然啦!客户给我们两个数字,我把它们加起来,然后求它们的积,然后用和除以积,然后如果结果大于10那么……
老V:你说得太绕了,我听不懂,给我换个清晰的说法!
小B:好的老大,这是我们部门内部的工作流程——客户依次给我们两个整数,为了描述方便,下文里把它们称为“a”和“b”(这就是所谓的形参!),首先我们求(a+b)/(a*b)这样一个表达式的值,存放在一个部门内部制定的变量s里,然后如果s大于10,我们返回10给客户;s不大于10的话,我们就直接把s的值告诉客户。
老V:很好,这下清楚多了。
...
【---下面是调用过程---】
客户甲:B经理,我这里有两个数字v1和v2(实参来了),麻烦你帮我算个结果出来。
小B:好的。唔,我看一下,v1是10,v2是15。秘书,告诉员工,按照部门的工作流程,把a替换成10,把b替换成15就行了!催他们干活快点!
...
客户乙:B经理,我这里有两个数字v3和v4(也是实参),麻烦你。。。
小B:不麻烦,你把v3、v4的值依次告诉我手下的员工,他们自然知道怎么处理!
【故事结束】
看懂了吗?简而言之,“形参”只是一个“形式”,在制定规则时指代“此处应有一个xxx”,而这个xxx到底值为多少,制定规则(部门工作流程)时是不知道的,需要在调用函数时靠实际的参数(客户)传进来。
(2) ByRef 和ByVal的区别
这个应该很好理解。打个比方,ByRef就是传“原件”,ByVal就是传“复印件”。如果一个人(函数)需要你的身份信息,你可以给他身份证原件,也可以给他复印件,他若不做修改,那这两种方式没区别;但他若要修改你的名字,那就区别很大了:ByRef时,相当于在原件上修改了你的名字,你的身份证就真的被改了;ByVal时,修改发生在复印件上,你的身份证原件丝毫不受影响。
(3) “返回值”的概念
这个相当好理解。比如s=s+1这样一个语句,如果我们写成函数的形式,是这样的:
'声明函数
Private Function addOne(ByVal n As Integer)
Return n+1
End Function
...
'调用函数
s = addOne(s)
这样应该看得很清楚了吧。
未完待续