VBA UserForm为其参数之一给出运行时错误91

Adr*_*ien 1 excel vba userform

我正在尝试通过Sub在excel-VBA中使用参数创建同一无模式UserForm的多个实例。

我可以使用我要分配的三个参数中的两个使用它,但是第三个参数一直在返回

“运行时错误'91':未设置对象变量或带块变量”

我不知道为什么。我可能没有看到明显的错字,但我确实无法指出问题所在。这是我的代码:

Sub AskToClose(targetWksht As Worksheet, targetRow As Integer, test As String)
    Dim newInstanceOfMe As Object

    Set newInstanceOfMe = UserForms.Add("MOVE_TO_CLOSED") 'MOVE_TO_CLOSED is the name of my UserForm

    newInstanceOfMe.targetWksht = targetWksht 'If I comment this line it works just fine, otherwise Run-time error 91
    newInstanceOfMe.targetRow = targetRow
    newInstanceOfMe.test = test

    newInstanceOfMe.Show vbModeless
End Sub

_____________________________________________________________________

Sub test()
    Dim openWksht As Worksheet

    Set openWksht = Worksheets("OPEN WO") 'The worksheet exists and works just fine everywhere else

    Call AskToClose(openWksht, 2, "test 2")
    Call AskToClose(openWksht, 3, "test 3")
    Call AskToClose(openWksht, 4, "test 4")

    Set openWksht = Nothing 'I tried to comment this line just in case, same result...
End Sub

_____________________________________________________________________
'My MOVE_TO_CLOSED UserForm parameters
Public targetWksht As Worksheet
Public targetRow As Integer
Public test As String
Run Code Online (Sandbox Code Playgroud)

Mat*_*don 5

newInstanceOfMe.targetWksht = targetWksht
Run Code Online (Sandbox Code Playgroud)

该语句为Rubberduck(我管理的开源VBIDE外接程序项目)中的“ 值要求”检查产生错误级别的代码质量检查结果。检查说明情况如下:

需要值的对象

如果在需要值类型的位置使用对象,并且对象的声明类型没有合适的默认成员,则VBA编译器不会引发错误。在几乎所有情况下,这都会导致运行时错误91“对象或未设置块变量”或438“对象不支持此属性或方法”,具体取决于对象是否具有值“ Nothing”,这很难检测到并表明存在错误。

VBA中有两种类型的分配:值分配Let)和引用分配Set)。Let对于值分配,关键字是冗余/可选/作废:

Dim foo As Long
foo = 2 + 2
Let foo = 2 + 2 '<~ exactly equivalent to the above
Run Code Online (Sandbox Code Playgroud)

因此,除非存在Set关键字,否则VBA会尝试进行值分配。如果对象具有默认成员,那可能就可以工作-VBA规范定义了强制转换机制如何实现这一点。这样便可以将a分配Range给值:

Sheet1.Range("A1") = 42
Run Code Online (Sandbox Code Playgroud)

这是通过对类的隐藏属性通过隐式成员调用来隐式分配给。Range.ValueRange.[_Default]Range

如果赋值的右侧也是一个对象,则在操作员的两侧都将发生强制转换:

Sheet1.Range("A1") = Sheet1.Range("B1") '<~ implicit default member calls galore!
Sheet1.Range("A1").Value = Sheet1.Range("B1").Value
Run Code Online (Sandbox Code Playgroud)

但是我们不在Range这里查看,我们在查看UserForm,并且a UserForm没有默认成员,因此let-coercion不会发生...但是编译器不会对此进行验证,因此代码可以仍然可以运行...而是在运行时爆炸。

因此,我们正在寻找一个Let分配,该分配的双方都持有一个对象引用,用于未定义默认成员的类类型。

Something.SomeObject = someOtherObject
Run Code Online (Sandbox Code Playgroud)

但是VBA并不在乎是否没有默认成员-因为没有Set关键字,它会尽最大努力按照您的指示进行操作,并将这些对象强制转换为值...显然会失败。

如果Something.SomeObject(左侧)为Nothing,则let-coercion尝试将尝试调用不存在的默认成员-但由于对象引用为Nothing,因此该调用无效,并引发错误91。

如果Something.SomeObject已经持有有效的对象引用,则let-coercion尝试将进一步进行,并由于没有默认成员要调用而失败,并显示错误438。

如果Something.SomeObject 具有默认成员(且引用不是Nothing),则值分配成功,不会引发错误...但是未分配对象引用,这可能是一个细微的错误!

添加Set关键字使该分配成为参考分配,现在一切正常。