类模块中公共Sub的参数使用用户定义类型(UDT)(VB6)

19

我已经尝试解决这个问题,但是找不到任何解决方案。我在一个普通模块中定义了一个UDT,并希望将其用作Class Module中的Public Sub的参数。然后我收到了编译错误:

只有在公共对象模块中定义的公共用户定义类型才能用作类模块的公共过程的参数或返回类型,或用作公共用户定义类型的字段。

然后我尝试将我的UDT移动到类中,并声明为Private。我得到了这个编译错误:

私有Enum和用户定义类型不能用作公共过程,公共数据成员或公共用户定义类型的字段的参数或返回类型。

最后,我尝试在类中声明它为Public,并收到了这个编译错误:

无法在私有对象模块中定义公共用户定义类型。

那么有没有办法让public UDT作为public sub的参数在class module中使用呢?

7个回答

18

只需将子程序定义为 Friend 范围即可。在 VB6 类中,这对我来说编译得很好。

Private Type testtype
  x As String
End Type


Friend Sub testmethod(y As testtype)

End Sub

从您的错误信息中可以看出,您的类是私有的。如果您确实希望您的类是公共的 - 例如,您正在创建一个ActiveX exe或DLL,并且希望客户端能够访问该子程序 - 那么只需将类型和子程序都设置为Public即可。


@MaikenRoskilde 并不是真的。在一般情况下,“friend”解决方案无法正常工作。将UDT制作成一个类会更加灵活:然后您可以拥有UDT类型的属性等。 - Felix Dombek
在VBA中,它似乎可以工作,但是Locals窗口显示其值为“<应用程序定义或对象定义错误>”,而不是显示其树形结构。 - 6diegodiego9

11

那么,有没有办法让一个公共UDT作为类中公共子程序的参数呢?

简而言之,没有。只使用Classic VB代码,你最接近的方法是创建一个复制UDT的类并使用它代替UDT。这样做有明显的优势,但如果你需要将其传递给API等,则无法实现。

另一种选择是在类型库中定义UDT。如果这样做,它可以用作公共方法的参数。


3
我想我有点晚了,但是... 我完全可以在我的Excel VBA应用程序中将公共UDT作为公共Sub(类)的参数。 - André Chalella
1
我来晚了,但是... @André Neves 我可以确认。然而,就我所知,UDT定义无法被封装起来,这很遗憾。 - Cool Blue
@Cool Blue,你说的UDT定义无法封装是什么意思?如果你们两个能帮我,那你们都不算“迟到”。因为我无法从类模块内的公共函数中返回在代码模块中定义的公共UDT。只是想澄清一下,我不能将该函数设置为Friend,因为我需要从C#应用程序调用它,并且它必须是公共函数。同时,我也不能将UDT作为ByRef参数传递给类函数。 - AllSolutions
1
@Karl,如何在类型库中定义UDT?我对此很陌生,你能详细解释一下或者提供一些链接吗? - AllSolutions

10

好的,如果我能让我的猫离开我,这就是如何做到的。

在Form1中(有一个命令按钮):

Option Explicit
'
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal dst As Long, ByVal src As Long, ByVal nBytes As Long)
'

Private Sub Command1_Click()
' Okay, this is what won't work in VB6:
'     Dim MyUdt1 As MyUdtType   ' Declare a variable with a publicly defined UDT (no problem).
'     Form2.Show                ' We could have created some object with a class.  This was just easier for the demo.
'           INSIDE OF FORM2:
'               Public Sub MySub(MyUdt2 As MyUdtType)   ' It won't even let you compile this.
'                   Msgbox MyUdt2.l
'                   MyUdt2.l = 5
'               End Sub
'     Form2.MySub MyUdt1                                ' You'll never get this far.
'     Unload Form2
'     Msgbox MyUdt1.l
'
' The following is a way to get it done:
'
Dim MyUdt1 As MyUdtType         ' Declare a variable with a publicly defined UDT (no problem).
Dim ReturnUdtPtr As Long        ' Declare a variable for a return pointer.
MyUdt1.l = 3                    ' Give the variable of our UDT some value.
Form2.Show                      ' Create our other object.
'
' Now we're ready to call our procedure in the object.
' This is all we really wanted to do all along.
' Notice that the VarPtr of the UDT is passed and not the actual UDT.
' This allows us to circumvent the no passing of UDTs to objects.
ReturnUdtPtr = Form2.MyFunction(VarPtr(MyUdt1))
'
' If we don't want anything back, we could have just used a SUB procedure.
' However, I wanted to give an example of how to go both directions.
' All of this would be exactly the same even if we had started out in a module (BAS).
CopyMemory VarPtr(MyUdt1), ReturnUdtPtr, Len(MyUdt1)
'
' We can now kill our other object (Unload Form2).
' We probably shouldn't kill it until we've copied our UDT data
' because the lifetime of our UDT will be technically ended when we do.
Unload Form2                    ' Kill the other object.  We're done with it.
MsgBox MyUdt1.l                 ' Make sure we got the UDT data back.
End Sub

在form2中(不需要控件)。 (这也可以是使用类创建的对象。):
    Option Explicit
'
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal dst As Long, ByVal src As Long, ByVal nBytes As Long)
'

Public Function MyFunction(ArgUdtPtr As Long) As Long
' Ok, this is how we get it done.
' There are a couple of things to notice right off the bat.
' First, the POINTER to the UDT is passed (using VarPtr) rather than the actual UDT.
' This way, we can circumvent the restriction of UDT not passed into objects.
' Second, the following MyUdt2 is declared as STATIC.
' This second point is important because the lifetime of MyUdt2 technically ends
' when we return from this function if it is just DIMmed.
' If we want to pass changes back to our caller, we will want to have a slightly longer lifetime.
Static MyUdt2 As MyUdtType
' Ok, we're here, so now we move the argument's UDT's data into our local UDT.
CopyMemory VarPtr(MyUdt2), ArgUdtPtr, Len(MyUdt2)
' Let's see if we got it.
MsgBox MyUdt2.l
' Now we might want to change it, and then pass back our changes.
MyUdt2.l = 5
' Once again, we pass back the pointer, because we can't get the actual UDT back.
' This is where the MyUdt2 being declared as Static becomes important.
MyFunction = VarPtr(MyUdt2)
End Function

最后,这段代码需要放在一个模块(BAS)文件中。
    Option Explicit
'
' This is just the UDT that is used for the example.
Public Type MyUdtType
    l As Long
End Type
'

3
纯粹的疯狂得到了一个赞;-) 我相信它会有效,但谁会使用它? - Dabblernl

1
我遇到了相同的错误信息,检查了应用程序后发现,在类的属性窗口中,“实例化”设置为所引用的对象的“1-私有”。我将其更改为“5-多重使用”,但仍然收到相同的错误消息。然后,我回到添加该引用对象之前的项目模块版本,并再次将其添加到项目中,它默认为“1-私有”。在进行任何其他操作之前,我将其更改为“5-多重使用”,并关闭项目以进行更新,然后编译。我重新打开项目,验证它仍设置为“5-多重使用”,然后编译该项目,无错误消息地编译通过。

当错误消息指出不允许引用私有对象时,该对象确实是私有的。一旦我声明它不是私有的,并且项目模块接受了该新设置,就可以干净地编译了。


1
只需将UDT作为引用参数传递即可,这样它就能正常工作。 :)
'method in the class

Public Sub CreateFile(ByRef udt1 As UdtTest)

End Sub

1
抱歉,我对这个有所期望,但它并不正确。 - Dabblernl

0
在模块中定义公共类型的用户定义函数 (UDF):
Public Type TPVArticulo
    Referencia As String
    Descripcion As String
    PVP As Double
    Dto As Double
End Type

在类、模块或窗体中使用Friend

Friend Function GetArticulo() As TPVArticulo

-4

UDT必须在公共对象中声明,例如:

Public Class Sample

    Public Strucutre UDT
       Dim Value As Object
    End Structure

End Class

3
这是VB.NET而不是VB6,你已经将类设为公共的了。在VB6中,只有当项目是ActiveX exe或DLL时才可能做到这一点 - 即使如此,提问者实际上也可能不想将类设为公共的。 - MarkJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接