考虑具有多个可选参数的函数。 E.g:
Function foo(Optional a, Optional b, Optional c, Optional d)
如果我只想调用特定的函数,参数不是null,或者是其他一些测试,(因为该函数会引发错误,除非参数缺失)。
一个例子是Collection类。 Add方法有3个可选参数。许多自定义类包装了这个类 - 比如创建一个可归档的类Persons of'Person'自定义类或类似的类。包装器必须处理6种组合:仅添加密钥,添加密钥并指定之前,添加密钥并指定After,不添加密钥并指定Before,不添加密钥并指定After,no Key和no Before /后。如果将包装器写入具有十几个可选参数的Workbook.Save之类的东西会更糟。
我不知道替代某些乏味的构造,如:
If a <> Null Then
If b <> Null Then
If c <> Null Then
If d <> Null Then
foo a, b, c, d
Else
foo a, b, c
End If
Else
If d <> Null Then
foo a, b, d
Else
foo a, b
End If
End If
Else
'... Etc ...
显然,嵌套的Ifs和一般代码开销的数量大约是每个额外可选变量的两倍。
在.NET中,可以传入类型System.Type.Missing,允许表达式更加简单。
与C#一样,它可以像以下一样干净:
foo(a ?? Missing, b ?? Missing, c ?? Missing, d ?? Missing);
(这表示'如果a为null则传递Missing(或者可能在幕后重构调用本身),相当于不传递任何参数的调用a等等)
如果它被实现,等效可以在VBA中使用内联iff('IIF(布尔值,真,假)')
我缺少VBA的解决方法吗?
答案 0 :(得分:4)
(编辑:我收录了HarveyFrench&#39;对代码的改进)
你可以减少嵌套:
Function foo(Optional a, Optional b, Optional c, Optional d)
Dim passed As String
If Not IsMissing(a) Then passed = "a "
If Not IsMissing(b) Then passed = passed & "b "
If Not IsMissing(c) Then passed = passed & "c "
If Not IsMissing(d) Then passed = passed & "d "
foo = IIf(Len(passed) = 0, "Nothing ", passed) & "passed"
End Function
Function foo_dispatcher(Optional a, Optional b, Optional c, Optional d)
Dim caseNum As Long
caseNum = IIf(IsNull(a) Or IsEmpty(a) Or IsMissing(a), 0, 8)
caseNum = caseNum + IIf(IsNull(b) Or IsEmpty(b) Or IsMissing(b), 0, 4)
caseNum = caseNum + IIf(IsNull(c) Or IsEmpty(c) Or IsMissing(c), 0, 2)
caseNum = caseNum + IIf(IsNull(d) Or IsEmpty(d) Or IsMissing(d), 0, 1)
Select Case caseNum
Case 0: foo_dispatcher = foo()
Case 1: foo_dispatcher = foo(, , , d)
Case 2: foo_dispatcher = foo(, , c)
Case 3: foo_dispatcher = foo(, , c, d)
Case 4: foo_dispatcher = foo(, b)
Case 5: foo_dispatcher = foo(, b, , d)
Case 6: foo_dispatcher = foo(, b, c)
Case 7: foo_dispatcher = foo(, b, c, d)
Case 8: foo_dispatcher = foo(a)
Case 9: foo_dispatcher = foo(a, , , d)
Case 10: foo_dispatcher = foo(a, , c)
Case 11: foo_dispatcher = foo(a, , c, d)
Case 12: foo_dispatcher = foo(a, b)
Case 13: foo_dispatcher = foo(a, b, , d)
Case 14: foo_dispatcher = foo(a, b, c)
Case 15: foo_dispatcher = foo(a, b, c, d)
End Select
End Function
Sub test()
Debug.Print foo_dispatcher(Null, Null, Null, Null)
Debug.Print foo_dispatcher(Null, 1, Null, 2)
Debug.Print foo_dispatcher(1, 2, 3, 4)
Debug.Print foo_dispatcher()
Debug.Print foo_dispatcher(, 1, , 2)
Debug.Print foo_dispatcher(a:=1, d:=Null)
End Sub
test
的输出:
Nothing passed
b d passed
a b c d passed
Nothing passed
b d passed
a passed
显然,16个案例中的行动可以根据foo
的调用惯例进行调整(例如,如果需要,您可以发送到foo(a,d)
而不是foo(a,,,d)
)。我没有明确检查所有16个案例,但似乎有效。我做的有点机械。您可以编写一个dispatch-generator - 一个函数,它接受一个字符串函数名,一个必需参数数组,一个可选参数数组,以及一个扮演Null
角色的值,并将调度程序作为字符串返回可以从即时窗口复制粘贴到代码模块。我想在这里做到这一点,但对于概念验证调度员而言,这似乎并不值得。
答案 1 :(得分:4)
John Coleman的回答是&#34;和VBA一样好,&#34;
我认为foo_dispatcher还需要对代码进行以下修改(在很多情况下):
Function foo_dispatcher(a, b, c, d)
Dim caseNum As Long
caseNum = IIf(IsNull(a) or IsEmpty(a) or IsMissing(a), 0, 8)
caseNum = caseNum + IIf(IsNull(b) or IsEmpty(b) or IsMissing(b), 0, 4)
caseNum = caseNum + IIf(IsNull(c) or IsEmpty(c) or IsMissing(c), 0, 4)
caseNum = caseNum + IIf(IsNull(d) or IsEmpty(d) or IsMissing(d), 0, 4)
' ...
并且测试用例还应包括
Sub test()
Debug.Print foo_dispatcher(,,,)
Debug.Print foo_dispatcher(, 1, , 2)
Debug.Print foo_dispatcher(a:=1,d:=Null)
End Sub