如何将范围obj变量传递给Excel VBA中的子(2016)

Had*_*iJo 0 excel vba excel-vba excel-2016

给出以下代码: 我似乎无法成功地将Range对象变量从一个子函数传递到另一个子函数.我花了整整一天时间研究和试验,然后才吞下骄傲来到这里.

请阅读下面的评论,并回答您有关为什么最后两行不会表现的任何想法.

Public Sub doSomethingToRows(ROI As Range)
*'do Something with the cell values within the supplied range*

End Sub
'
Public Sub testDoAltRows()

    Dim RegionOfInterest As Range       'is this an object or not?

    '*The following yields: Class doesn't support Automation (Error 430)*
    '*Set RegionOfInterest = New Worksheet 'this just gives an error*

    Set RegionOfInterest = Worksheets("Sheet1").Range("A1")
    RegionOfInterest.Value = 1234.56        '*okay, updates cell A1*

    Set RegionOfInterest = Worksheets("Sheet1").Range("B5:D15")
    RegionOfInterest.Columns(2).Value = "~~~~~~"    '*okay*

    'doSomethingToRows (RegionOfInterest)   'why do I get "OBJECT IS REQUIRED" error?
    doSomethingToRows (Worksheets("Sheet1").Range("B5:C15")) 'but this executes okay
End Sub
Run Code Online (Sandbox Code Playgroud)

Bla*_*awk 5

Call关键字语句msdn文档中,

备注

调用过程时,不需要使用Call关键字.但是,如果使用Call关键字调用需要参数的过程,则必须将参数列表括在括号中.如果省略Call关键字,则还必须省略argumentlist周围的括号.如果使用Call语法调用任何内部函数或用户定义函数,则会丢弃函数的返回值.

要将整个数组传递给过程,请使用数组名称后跟空括号.

从实际的角度来看,即使可以使用或不使用"Call"关键字调用Subs,但选择一种方式并坚持使用它作为编码风格的一部分是有意义的.我同意Comintern的观点 - 基于对现代VBA代码的观察,我认为使用"Call"关键字应该被视为已弃用.相反,在参数列表周围调用不带括号的Subs.

现在回答这个重要问题:

为什么你的代码会抛出错误?

以下面的子例程为例:

Public Sub ShowSum(arg1 As Long, arg2 As Long)
    MsgBox arg1 + arg2
End Sub
Run Code Online (Sandbox Code Playgroud)

我们已经确定,如果不使用Call关键字,必须像这样调用Subs:

ShowSum 45,37

如果它被称为是什么会发生什么ShowSum(45, 37)?好吧,你甚至无法编译,因为VBA立即抱怨"预期=".这是因为VBA解析器看到括号并确定它必须是一个Function调用,因此它希望您使用"="赋值语句处理返回值.

那只有一个参数的Sub怎么样?例如:

Public Sub ShowNum(arg1 As Long)
    MsgBox arg1
End Sub
Run Code Online (Sandbox Code Playgroud)

调用此Sub的正确方法是ShowNum 45.但是如果你在VBA IDE中键入它ShowNum(45)会怎么样?一旦将光标移出线条,您就会注意到VBA在子名称和左括号之间添加了一个空格,为您提供关于如何实际解释代码行的关键线索:

ShowNum (45)
Run Code Online (Sandbox Code Playgroud)

VBA并未将这些括号视为包围参数列表 - 而是将它们视为分组括号.大多数情况下,这都无关紧要,但在具有默认成员的对象中也是如此.

要查看此问题,请尝试运行以下命令:

Dim v As Variant
Set v = Range("A1")
Set v = (Range("A1"))  '<--- type mismatch here
Run Code Online (Sandbox Code Playgroud)

请注意,标记的行上出现"类型不匹配".现在将这两个语句添加到监视窗口并查看"类型"列:

+-------------+-----+--------------+
| Expression  |Value|     Type     |
+-------------+-----+--------------+
|Range("A1")  |     |Object/Range  |
|(Range("A1"))|     |Variant/String|
+-------------+-----+--------------+
Run Code Online (Sandbox Code Playgroud)

当您使用分组括号包围对象时,将评估其默认属性 - 对于Range对象,它是Value属性.

因此,VBA允许你在"将参数列表括号括起来"中实际上只是巧合 - 实际上,VBA只是将其解释为分组括号并相应地评估该值.您可以通过在Sub上尝试相同的事情来查看它在VBA中无效的多个参数,以调用参数列表周围带括号的Sub.

@PaulG

试试这个:

Public Sub Main()
    Debug.Print TypeName(Range("A1"))
    Debug.Print TypeName((Range("A1")))
End Sub
Run Code Online (Sandbox Code Playgroud)

  • @PaulG我会留下它 - 我们已经评论了可怜的OP的问题来回复:P但是我会给你一个更多的资源:[VBA语言规范(VBAL)](http://interoperability.blob .core.windows.net /文件/ MS-VBAL/[MS-VBAL] .PDF).有关VBA如何执行隐式转换的更多信息,请参见第5.5节. (2认同)
  • @PaulG关键是问题,无关的parens会导致他们的内容被**评估**之前结果被传递给被调用的程序(并且可能被强制转换为参数的类型).此外,没有冒犯,但Blackhawk和Comintern知道他们的VBA内部(完全字面意义),并且`Call`是已弃用/过时的语法,绝对[零有效用例](http://stackoverflow.com/a/22325284/ 1188513)在本世纪编写的任何代码中.我不确定所有这些争论的内容是什么,但我正在将这整个混乱标记为清理. (2认同)