【发布时间】:2018-02-11 09:14:19
【问题描述】:
问题
当您想直接查看 UDF 的参数(不是它们的 值,可以直接传递,而是 给出这些值的公式),您可以使用 Application.Caller.Formula 并解析出参数找出来。
有什么方法可以查看调用函数的那行 VBA 代码,以便您可以以类似的方式解析出它的参数?
背景
不久前,我创建了一个 UDF,它本质上是数组函数的另一种方法*。我想做的是采取一些评估为True/False
LEN(A1)>LEN(B1)
并在一个数组上评估它。所以说上面的函数放在单元格A1中,那么对数组A1:A100求值就和创建数组一样
{LEN(A1)>LEN(B1),LEN(A2)>LEN(B2),[...]} 'you may recognise this as an array formula ={LEN(A1:A100)>LEN(B1:B100)}
*对于上下文,这是在我了解数组公式之前
我对某些数组处理 Excel 函数的语法感到沮丧,例如 COUNTIF,它采用以下形式的参数
COUNTIF(range_To_Evalueate_Over, "string_Representing_Boolean_Test")
字符串参数存在以下限制
- 任何布尔返回语句都不能用作测试;除了它们的值之外,没有办法查看您评估的范围的属性
- 因此您不能使用像 LEN() 这样的函数来获取有关范围的更多数据
- 您不能引用相对于该范围的其他单元格(例如
B1相对于A1)
- 该字符串在运行时是静态的,您无法单步执行该函数来查看该字符串对于您正在评估的范围内的给定单元格的评估结果
我更喜欢条件格式公式的多功能性。它们采用数组公式的形式,其中任何偏移量(B1 相对于 A1)都是相对于应用条件格式的范围的 TL 单元格计算的。
这促使我创建了一个具有类似结构的 UDF
evaluateOverRange(range_to_evalute_over As Range, boolean_test_on_TL_Cell As Boolean) As Boolean() 'returns an array equal in size to the evaluate range
类似
evaluateOverRange(A1:A100,LEN(A1)<LEN(B1))
注意
- 布尔测试不是字符串,因此可以在 Excel 中逐步计算
- 由于类型声明,布尔测试保证为布尔型
- 布尔测试与评估范围 (
A1:A100) 中的第一个单元格 (A1) 相关- 即
B1替换为A1.Offset(0,1)
- 即
- 由于
boolean_test_on_TL_Cell不是字符串,它没有告诉我们实际测试的任何信息,它只是通过了A1上的测试结果,它在UDF 中实际上是无用的,因此被忽略- 获取测试字符串
"LEN(A1)<LEN(B1)",读取Application.Caller.Formula,解析出evaluateOverRange的相关参数
- 获取测试字符串
为了在 VBA 中通过数组评估某些工作表函数,您可以使用 Evaluate 方法
Dim colA As Range: Set colA = [A1:A100] 'range_to_evaluate_over in my udf
Dim cellA As Range
Dim cellB as Range
Dim outputArray(1 To 100) As Boolean
For i = 1 To 100
Set cellA = colA(i)
Set cellB = cellA.Offset(0,1) 'all cells that arent the TL cell in colA (i.e., not A1) are set relative to the top left cell
outputArray(i) = Evaluate("LEN(" & cellA.Value & ")>LEN(" & cellB.Value ")")
Next i
是的,所有这些都是针对工作表函数的,而给定的数组函数也做同样的事情。但现在我想在 VBA 中使用相同的方法。
具体来说,我想使用实际的 VBA 布尔返回代码而不是字符串,根据其属性的某些功能过滤自定义类的数组。
Sub FilterMyClassArray() 'Prints how many items in arrayToFilter whose properties match certain conditions
Dim arrayToFilter(1 To 100) As New myClass
Dim filteredArray() As myClass
Dim tlClass As myClass 'pretend class used only for intellisense and to create
boolean test
Set filteredArray = filterClassArray(arrayToFilter, tlClass.PropertyA > 3 And
tlClass.PropertyB = "hat")
Debug.Print "Number left after filtering:" ; Ubound(filteredArray)
End Sub
Function filterClassArray(ByVal inutArray() As myClass, classTest As Boolean) As myClass 'returns an output array which is equal to the input array filtered by some test
'Somehow get what classTest actually was
'Evaluate classTest over each item in inputArray
'If boolean test evaluates to true, add to output array, otherwise skip
End Function
我想需要对代码模块进行一些操作(既要获取代表测试的代码字符串,又要实际评估它),但我想在深入研究之前检查可行性。
【问题讨论】:
-
如果你以字符串而不是布尔表达式的形式通过测试,你的任务会更简单(好吧,更简单一点):按照你展示的方式做这件事还有很长的路要走到达同一点,因为您期望以某种方式解析调用代码以提取测试基础的文本。既然可以一开始就以字符串的形式通过测试,为什么还要进行往返呢?
-
@TimWilliams 感谢您阅读本文!往返是出于同样的原因,我不想将信息作为字符串传递给我的工作表函数。即:如果我使用 live 表达式而不是字符串,我可以使用智能感知并进入函数。在较小程度上,它还保证了带有类型声明的测试的有效性。这个想法是,如果我现在完成大部分的跑腿工作,这将节省我的时间并且在未来更加有用。
-
第一个解决方法是首先输入代码,然后转换为字符串。对于后者,我可以只使用即时窗口。但我认为您可以看到不必这样做的潜在好处
-
这对我来说似乎需要做更多的工作 - 如果您不是动态地执行此操作(在运行时根据用户输入构建过滤器),那么考虑到您的箍,我看不到太多好处需要跳过才能使其正常工作。
-
我真的不认为这是可能的。一方面,VBA 不支持反射,因此您必须自己将属性名称转换为字符串;另一方面,您将需要使用
CallByName()函数来获取值,然后使用Evaluate()来获取结果,即使这样Evaluate使用 Excel 公式语法,例如,您需要将... And ...语法转换为AND(..., ...)并且纯粹为了通过智能感知获取属性而实例化一个新的类对象似乎很奇怪。我想说这需要重新考虑。
标签: arrays vba excel excel-formula