说到Excel的SUM函数,我估计只要用过Excel的,应该没人不知道了,SUM函数多简单啊,点一下自动求和,自动就能定位好范围,回车就完成了。
是的,SUM函数用起来太简单、太方便了,让我们自己用VBA来实现看看,你会发现SUM函数也有它不简单的一面。
1、不简单的SUM第1参数:
SUM函数的参数提示上,number1是一定要有的,这个number1我们在Excel里使用的时候可能看不出它有什么特殊,那是因为我们没有去仔细想过。我们现在知道了VBA Function参数,我们再仔细想想这个number1有什么特殊之处:
- 可以直接输数字
- 可以直接输非数字的文本(会返回#VALUE!)
- 可以输1个单元格或者1个单元格范围
它可以允许我们很方便的输入许多种情况,说明的是它实现过程都考虑到了这些情况,并能够解析。
2、不定长参数ParamArray:
你注意看SUM函数的参数提示,应该能注意到,它的参数最后有“...”这个东西,我们在前面没有说到过,不过,在Function函数里,我们让大家去看官方文档,如果仔细看了,里面有这么一个东西:
代码语言:javascript复制ParamArray 可选。仅用作 arglist 中的最后一个参数来指示最后的参数为 Variant 元素的 Optional 数组。
ParamArray 关键字允许您提供任意数量的参数。
它无法用于 ByVal、ByRef 或 Optional
ParamArray修饰符,是一种不定长的参数,就是你在函数中参数用逗号分隔开,函数内部收到的其实就是1个数组,所以我们在用SUM函数的时候,你可以不停的用逗号分隔需要相加的单元格,函数都能正确的返回。
这种修饰符给代码的编写提供了极大的便利,像C语言里的printf函数一样。
SUM函数的不定长参数也和第1个参数一样,可以输入多种形式。
3、代码实现
通过上面的介绍,解析number1的各种输入形式是比较重要的,所以我们把解析单独做成1个函数:
代码语言:javascript复制Function ParseValue(num1 As Variant) As Variant
Dim dsum As Double
Select Case VBA.VarType(num1)
Case vbEmpty, vbNull, vbObject, vbError
dsum = 0
Case vbInteger, vbLong, vbSingle, vbDouble, vbDate, vbBoolean, vbByte
dsum = VBA.CDbl(num1)
Case vbString
If VBA.IsNumeric(num1) Then
dsum = VBA.CDbl(num1)
Else
ParseValue = "#VALUE!"
Exit Function
End If
Case Else
dsum = 0
End Select
ParseValue = dsum
End Function
VarType返回变量的数据类型,具体参看官方文档,这里没有使用if判断,而改用了Select Case,这种和if的逻辑差不多,但更方便判断这种多类型的。
程序通过判断num1的数据类型来决定如何处理:
- 像vbError这种都当作0处理
- vbString为了和Excel的SUM相同进行了一些特殊判断,使用IsNumeric判断它是否是纯数字的文本,是的情况转化为数字处理,否则返回错误#VALUE!
- 其他我们只简单处理了数据类型
这里故意没有去处理数组类型,因为一旦在这里处理数组类型,就需要用到递归了,递归这个东西对写程序很重要,我觉得就相当于学函数需要会相对引用和绝对引用以及数组公式一样。但是初学者一般很难理解,有兴趣的可以上网查一些资料学习。
数组的处理放到了主函数里:
代码语言:javascript复制Function MySum(num1 As Variant, ParamArray argcs() As Variant) As Variant
Dim dsum As Double
Dim v As Variant
Dim tmp As Variant
If VBA.IsArray(num1) Then
For Each v In num1
tmp = ParseValue(v)
If tmp = "#VALUE!" Then
MySum = tmp
Exit Function
End If
dsum = dsum tmp
Next
Else
tmp = ParseValue(num1)
If tmp = "#VALUE!" Then
MySum = tmp
Exit Function
End If
dsum = dsum tmp
End If
Dim vv As Variant
For Each v In argcs
If VBA.IsArray(v) Then
For Each vv In v
tmp = ParseValue(vv)
If tmp = "#VALUE!" Then
MySum = tmp
Exit Function
End If
dsum = dsum tmp
Next
Else
tmp = ParseValue(v)
If tmp = "#VALUE!" Then
MySum = tmp
Exit Function
End If
dsum = dsum tmp
End If
Next
MySum = dsum
End Function
IsArray函数能判断参数是否是1个数组,如果是数组,我们就用For Each 遍历其中的每一个元素,并调用ParseValue函数进行处理。
注:这里其实也不够严谨,理论上数组里的元素仍然可以是数组,不停的延续下去,这种最好就是用递归处理的。
测试代码:
代码语言:javascript复制Sub TestMySum()
Dim arr(1) As Integer
arr(0) = 2
arr(1) = 8
Debug.Print MySum(Range("C2"), 1, 2, 3, arr)
End Sub
输出:
26 (Range("C2") = 10)
4、小结
实现了一个非常简单的SUM函数,多种形式输入的参数解析,不定长参数的传递和使用。