VBA
Passing Arguments ByRef eller ByVal
Sök…
Introduktion
ByRef
och ByVal
modifierarna ingår i procedurens signatur och anger hur ett argument överförs till en procedur. I VBA passeras en parameter ByRef
inte annat anges (dvs. ByRef
är implicit om det inte finns).
Obs! På många andra programmeringsspråk (inklusive VB.NET) överförs parametrar implicit genom värde om ingen modifierare anges: överväga att specificera ByRef
modifierare uttryckligen för att undvika eventuell förvirring.
Anmärkningar
Passerar matriser
Matriser måste skickas som referens. Den här koden kompilerar, men höjer körfel 424 "Objekt krävs":
Public Sub Test()
DoSomething Array(1, 2, 3)
End Sub
Private Sub DoSomething(ByVal foo As Variant)
foo.Add 42
End Sub
Den här koden sammanställer inte:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
Passing Simple Variables ByRef And ByVal
Att passera ByRef
eller ByVal
indikerar om det verkliga värdet på ett argument överförs till CalledProcedure
av CallingProcedure
, eller om en referens (kallas en pekare på vissa andra språk) skickas till CalledProcedure
.
Om ett argument passeras ByRef
, skickas ByRef
minnesadress till CalledProcedure
och alla ändringar av den parametern av CalledProcedure
görs till värdet i CallingProcedure
.
Om ett argument passeras ByVal
, ByVal
det verkliga värdet, inte en referens till variabeln, till CalledProcedure
.
Ett enkelt exempel illustrerar detta tydligt:
Sub CalledProcedure(ByRef X As Long, ByVal Y As Long)
X = 321
Y = 654
End Sub
Sub CallingProcedure()
Dim A As Long
Dim B As Long
A = 123
B = 456
Debug.Print "BEFORE CALL => A: " & CStr(A), "B: " & CStr(B)
''Result : BEFORE CALL => A: 123 B: 456
CalledProcedure X:=A, Y:=B
Debug.Print "AFTER CALL = A: " & CStr(A), "B: " & CStr(B)
''Result : AFTER CALL => A: 321 B: 456
End Sub
Ett annat exempel:
Sub Main()
Dim IntVarByVal As Integer
Dim IntVarByRef As Integer
IntVarByVal = 5
IntVarByRef = 10
SubChangeArguments IntVarByVal, IntVarByRef '5 goes in as a "copy". 10 goes in as a reference
Debug.Print "IntVarByVal: " & IntVarByVal 'prints 5 (no change made by SubChangeArguments)
Debug.Print "IntVarByRef: " & IntVarByRef 'prints 99 (the variable was changed in SubChangeArguments)
End Sub
Sub SubChangeArguments(ByVal ParameterByVal As Integer, ByRef ParameterByRef As Integer)
ParameterByVal = ParameterByVal + 2 ' 5 + 2 = 7 (changed only inside this Sub)
ParameterByRef = ParameterByRef + 89 ' 10 + 89 = 99 (changes the IntVarByRef itself - in the Main Sub)
End Sub
ByRef
Standardmodifierare
Om ingen modifierare anges för en parameter, överförs den parametern implicit genom referens.
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
foo
parametern överförs ByRef
i både DoSomething1
och DoSomething2
.
Se upp! Om du kommer till VBA med erfarenhet från andra språk är det mycket troligtvis det motsatta beteendet än det du är van vid. På många andra programmeringsspråk (inklusive VB.NET) överför den implicita / standardmodifieraren parametrar efter värde.
Skickas genom referens
När ett värde passeras
ByRef
får proceduren en referens till värdet.Public Sub Test() Dim foo As Long foo = 42 DoSomething foo Debug.Print foo End Sub Private Sub DoSomething(ByRef foo As Long) foo = foo * 2 End Sub
Anropa ovan
Test
procedur utmatar 84.DoSomething
gesfoo
och mottar en referens till värdet, och arbetar därför med samma minnesadress som den som ringer.När en referens passeras
ByRef
får proceduren en referens till pekaren.Public Sub Test() Dim foo As Collection Set foo = New Collection DoSomething foo Debug.Print foo.Count End Sub Private Sub DoSomething(ByRef foo As Collection) foo.Add 42 Set foo = Nothing End Sub
Ovanstående kod väcker körtidfel 91 , eftersom uppringaren ringer
Count
för ett objekt som inte längre finns, eftersomDoSomething
fick en referens till objektpekaren och tilldelades det tillNothing
innan han återvände.
Tvinga ByVal på samtalssidan
Med hjälp av parenteser på samtalssidan kan du åsidosätta ByRef
och tvinga ett argument att skickas ByVal
:
Public Sub Test()
Dim foo As Long
foo = 42
DoSomething (foo)
Debug.Print foo
End Sub
Private Sub DoSomething(ByRef foo As Long)
foo = foo * 2
End Sub
Ovanstående kod matar ut 42, oavsett om ByRef
anges implicit eller uttryckligen.
Se upp! På grund av detta kan användning av främmande parenteser i procedursamtal enkelt införa buggar. Var uppmärksam på mellanrummet mellan procedurnamnet och argumentlistan:
bar = DoSomething(foo) 'function call, no whitespace; parens are part of args list DoSomething (foo) 'procedure call, notice whitespace; parens are NOT part of args list DoSomething foo 'procedure call does not force the foo parameter to be ByVal
ByVal
Passerar efter värde
När ett värde passeras
ByVal
får proceduren en kopia av värdet.Public Sub Test() Dim foo As Long foo = 42 DoSomething foo Debug.Print foo End Sub Private Sub DoSomething(ByVal foo As Long) foo = foo * 2 End Sub
Anropa ovan
Test
procedur utmatar 42.DoSomething
gesfoo
och mottar en kopia av värdet. Kopian multipliceras med 2 och kastas sedan när proceduren går ut. samtalens kopia ändrades aldrig.När en referens passeras
ByVal
får proceduren en kopia av pekaren.Public Sub Test() Dim foo As Collection Set foo = New Collection DoSomething foo Debug.Print foo.Count End Sub Private Sub DoSomething(ByVal foo As Collection) foo.Add 42 Set foo = Nothing End Sub
Anropa ovan
Test
utmatar 1.DoSomething
gesfoo
och mottar en kopia av pekaren tillCollection
objektet. Eftersomfoo
objektvariabeln iTest
omfattning pekar på samma objekt, lägga ett objekt iDoSomething
adderar objektet till samma objekt. Eftersom det är en kopia av pekaren påverkar inte inställarens egna kopia att ställa in referensen tillNothing
.