如何将变量分配给VBA中的变量?
(警告:虽然乍一看可能像一个问题,但这不是初级问题。如果您熟悉短语“让强制”或您曾经研究过VBA规范,请继续阅读。) 假设我有一个如何将变量分配给VBA中的变量?,vba,types,variant,Vba,Types,Variant,(警告:虽然乍一看可能像一个问题,但这不是初级问题。如果您熟悉短语“让强制”或您曾经研究过VBA规范,请继续阅读。) 假设我有一个Variant类型的表达式,我想把它赋给一个变量。听起来很简单,对吧 Dim v As Variant v = SomeMethod() ' SomeMethod has return type Variant 不幸的是,如果SomeMethod返回一个对象(即,带有VarType的vbObject的变量),则会启动并v包含该对象的“简单数据值”。换句话说,
Variant
类型的表达式,我想把它赋给一个变量。听起来很简单,对吧
Dim v As Variant
v = SomeMethod() ' SomeMethod has return type Variant
不幸的是,如果SomeMethod
返回一个对象(即,带有VarType的vbObject的变量),则会启动并v
包含该对象的“简单数据值”。换句话说,如果SomeMethod返回对文本框的引用,v
将包含一个字符串
显然,解决方案是使用Set
:
Dim v As Variant
Set v = SomeMethod()
不幸的是,如果SomeMethod
不返回对象(例如字符串),则此操作将失败,从而产生类型不匹配错误
到目前为止,我找到的唯一解决方案是:
Dim v As Variant
If IsObject(SomeMethod()) Then
Set v = SomeMethod()
Else
v = SomeMethod()
End If
这会产生不幸的副作用,调用SomeMethod
两次
有没有一种解决方案不需要调用
SomeMethod
两次?在VBA中,在不知道变量是对象还是原语的情况下,将变量指定给变量的唯一方法是将其作为参数传递
如果无法重构代码,从而将v
作为参数传递给Sub、函数或Let属性(尽管Let
也适用于对象),则始终可以在模块范围内声明v
,并拥有一个专用Sub,仅用于保存和分配该变量:
Private v As Variant
Private Sub SetV(ByVal var As Variant)
If IsObject(var) Then
Set v = var
Else
v = var
End If
End Sub
在其他地方调用SetV SomeMethod()
不太好看,但这是唯一一种不调用SomeMethod()
两次或不触及其内部工作的方法
编辑 好吧,我仔细考虑了一下,我想我找到了一个更好的解决方案,更接近你的想法:
Public Sub LetSet(ByRef variable As Variant, ByVal value As Variant)
If IsObject(value) Then
Set variable = value
Else
variable = value
End If
End Sub
[…]我想根本就没有LetSet v=。。。VBA中的语句
现在有:letv,SomeMethod()
您不需要根据变量的类型将返回值设置为变量,而是通过引用传递应包含返回值的变量作为第一个参数,以便子对象可以更改其值。您可以使用错误捕获来减少预期的方法调用数。首先,试着设置。如果成功,没问题。否则,只需分配:
Public counter As Long
Function Ambiguous(b As Boolean) As Variant
counter = counter + 1
If b Then
Set Ambiguous = ActiveSheet
Else
Ambiguous = 1
End If
End Function
Sub test()
Dim v As Variant
Dim i As Long, b As Boolean
Randomize
counter = 0
For i = 1 To 100
b = Rnd() < 0.5
On Error Resume Next
Set v = Ambiguous(b)
If Err.Number > 0 Then
Err.Clear
v = Ambiguous(b)
End If
On Error GoTo 0
Next i
Debug.Print counter / 100
End Sub
公共计数器的长度
函数不明确(b作为布尔值)作为变量
计数器=计数器+1
如果b那么
Set=ActiveSheet
其他的
不明确=1
如果结束
端函数
子测试()
Dim v作为变体
Dim i等于长,b等于布尔值
随机化
计数器=0
对于i=1到100
b=Rnd()<0.5
出错时继续下一步
设置v=不明确(b)
如果错误编号>0,则
呃,明白了
v=不明确(b)
如果结束
错误转到0
接下来我
调试。打印计数器/100
端接头
当我运行代码时,第一次得到1.55,这比你重复实验时得到的2.00要小,但是错误处理方法被你在问题中讨论的天真的if-then-else
方法所取代
请注意,函数返回对象的频率越高,平均而言函数调用的次数就越少。如果它几乎总是返回一个对象(例如,这是它应该返回的对象,但在某些情况下返回一个描述错误条件的字符串),那么这种方法将接近每设置/分配一个变量调用一次。另一方面,如果它几乎总是返回一个基元值,那么每个赋值将接近2个调用,在这种情况下,也许您应该重构代码。
答案已经给了我
简言之:
Public Declare Sub VariantCopy Lib "oleaut32.dll" (ByRef pvargDest As Variant, ByRef pvargSrc As Variant)
Sub Main()
Dim v as Variant
VariantCopy v, SomeMethod()
end sub
这似乎类似于答案中描述的
LetSet()
函数,但我认为这无论如何都是有用的。您可以将SomeMethod
更改为sub-SomeMethod(变量为var):设置var=xxx…
并使用SomeMethod v
@AlexK指定给v byref样式。:巧合的是,虽然出于其他原因(我需要类似于.NET的TryParse/TryGet模式的东西,所以我让SomeMethod返回一个布尔值并使用ByRef作为变量),但这正是我最终使用的方法。我仍然需要在SomeMethod中进行丑陋的IsObject检查,这让我有点困扰,但我想VBA中没有LetSet v=…
语句。另一种语法是将LetSet
视为(参数化)属性,允许您执行LetSet(v)=SomeMethod()
-例如,对于代码,该方法可以返回特定对象。您需要将方法调用强制转换为一个变量以避免崩溃:VariantCopy v,CVar(SomeMethod())@CristianBuse真的吗?你能举一个这样一个物体的例子吗?我以前没有经历过这种情况,但我想知道是什么导致了崩溃:)为没有正确测试而道歉。对于VariantCopy API的声明,我使用了“Function”而不是“Sub”,并且我还缺少了损害API的返回类型(只要)。使用您的声明可以很好地工作。
Dim v As Variant
For Each v In Array(SomeMethod())
Exit For 'Needed for v to retain it's value
Next v
'Use v here - v is now holding a value or a reference
Dim v As Variant
Dim a As Variant
a = Array(SomeMethod())
If IsObject(a(0)) Then
Set v = a(0)
Else
v = a(0)
End If