Byval byref vba excel

Вступление

Модификаторы ByRef и ByVal являются частью сигнатуры процедуры и указывают, как передается аргумент процедуре. В VBA параметр передается ByRef если не указано иное (т.е. ByRef неявно, если отсутствует).

Примечание. Во многих других языках программирования (включая VB.NET) параметры неявно передаются по значению, если не указан модификатор: подумайте о том, чтобы указать модификаторы ByRef явно, чтобы избежать возможной путаницы.

замечания

Передача массивов

Массивы должны передаваться по ссылке. Этот код компилируется, но вызывает ошибку времени выполнения 424 «Object Required»:

Public Sub Test()
    DoSomething Array(1, 2, 3)
End Sub

Private Sub DoSomething(ByVal foo As Variant)
    foo.Add 42
End Sub

Этот код не компилируется:

Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
    foo.Add 42
End Sub

Передача ByRef или ByVal указывает, ByVal ли фактическое значение аргумента CalledProcedure CallingProcedure , или же ссылка (называемая указателем на некоторых других языках) передается CalledProcedure .

Если аргумент передан ByRef , адрес памяти аргумента передается в CalledProcedure и любая модификация этого параметра CalledProcedure выполняется в значение CallingProcedure .

Если аргумент передается ByVal , фактическое значение, а не ссылка на переменную, передается в CalledProcedure .

Простой пример будет ясно иллюстрировать это:

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

Другой пример:

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


Модификатор по умолчанию

Если для параметра не указан модификатор, этот параметр неявно передается по ссылке.

Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub

Параметр foo передается ByRef как в DoSomething1 и DoSomething2 .

Осторожно! Если вы приходите в VBA с опытом работы на других языках, это, скорее всего, совершенно противоположное поведение с тем, к которому вы привыкли. Во многих других языках программирования (включая VB.NET) неявный / по умолчанию модификатор передает параметры по значению.


Передача по ссылке

  • Когда значение передается ByRef , процедура получает ссылку на значение.

    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
    

    Вызов вышеуказанных выходов Test процедуры 84. DoSomething присваивается foo и получает ссылку на значение, и поэтому работает с тем же адресом памяти, что и вызывающий.

  • Когда ссылка передается ByRef , процедура получает ссылку на указатель.

    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
    

    Вышеприведенный код повышает ошибку 91 во время выполнения , поскольку вызывающий абонент вызывает член Count объекта, который больше не существует, поскольку DoSomething получил ссылку на указатель объекта и назначил его Nothing перед возвратом.


Принуждение ByVal на сайте вызова

Используя круглые скобки на сайте вызова, вы можете переопределить ByRef и принудительно передать аргумент 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

Вышеуказанные выходы кода 42, независимо от того, указан ли ByRef неявно или явно.

Осторожно! Из-за этого, используя посторонние круглые скобки в процедурных вызовах, можно легко ввести ошибки. Обратите внимание на пробелы между именем процедуры и списком аргументов:

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

Передача по значению

  • Когда значение передается ByVal , процедура получает копию значения.

    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
    

    Вызов вышеуказанных выходов Test процедуры 42. DoSomething присваивается foo и получает копию значения. Копия умножается на 2, а затем отменяется, когда процедура завершается; копия вызывающего абонента никогда не изменялась.

  • Когда ссылка передается ByVal , процедура получает копию указателя.

    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
    

    Вызов вышеуказанных результатов Test процедуры 1. DoSomething предоставляется foo и получает копию указателя на объект Collection . Поскольку переменная объекта foo в области Test указывает на один и тот же объект, добавление элемента в DoSomething добавляет элемент к одному и тому же объекту. Поскольку это копия указателя, установка ссылки на Nothing не влияет на собственную копию вызывающего абонента.

In this Article

  • Creating a Function without Arguments
  • Calling a Function from a Sub Procedure
  • Creating Functions
    • Single Argument
    • Multiple Arguments
    • Optional Arguments
    • Default Argument Value
    • ByVal and ByRef
  • Exit Function
  • Using a Function from within an Excel Sheet

This tutorial will teach you to create and use functions with and without parameters in VBA

VBA contains a large amount of built-in functions for you to use, but you are also able to write your own.   When you write code in VBA, you can write it in a Sub Procedure, or a Function Procedure. A Function Procedure is able to return a value to your code.  This is extremely useful if you want VBA to perform a task to return a result. VBA functions can also be called from inside Excel, just like Excel’s built-in Excel functions.

Creating a Function without Arguments

To create a function you need to define the function by giving the function a name. The function can then be defined as a data type indicating the type of data you want the function to return.

You may want to create a function that returns a static value each time it is called – a bit like a constant.

Function GetValue() As Integer
   GetValue = 50
End Function

If you were to run the function, the function would always return the value of 50.

vba function no argument

You can also create functions that refer to objects in VBA but you need to use the Set Keyword to return the value from the function.

Function GetRange() as Range
  Set GetRange = Range("A1:G4")
End Function

If you were to use the above function in your VBA code, the function would always return the range of cells A1 to G4 in whichever sheet you are working in.

Calling a Function from a Sub Procedure

Once you create a function, you can call it from anywhere else in your code by using a Sub Procedure to call the function.

vba function no argument 1

The value of 50 would always be returned.

You can also call the GetRange function from a Sub Procedure.

vba function no argument range

In the above example, the GetRange Function is called by the Sub Procedure to bold the cells in the range object.

Creating Functions

Single Argument

You can also assign a parameter or parameters to your function.  These parameters can be referred to as Arguments.

Function ConvertKilosToPounds (dblKilo as Double) as Double
   ConvertKiloToPounds = dblKilo*2.2
End Function

We can then call the above function from a Sub Procedure in order to work out how many pounds a specific amount of kilos are.

vba function return value

A function can be a called from multiple procedures within your VBA code if required.  This is very useful in that it stops you from having to write the same code over and over again.  It also enables you to divide long procedures into small manageable functions.

vba functions return values 1

In the above example, we have 2 procedures – each of them are using the Function to calculate the pound value of the kilos passed to them in the dblKilo Argument of the function.

Multiple Arguments

You can create a Function with multiple arguments and pass the values to the Function by way of a Sub Procedure.

Function CalculateDayDiff(Date1 as Date, Date2 as Date) as Double
   CalculateDayDiff = Date2-Date1
End Function

We can then call the function to calculate the amount of days between 2 dates.

vba-function-2-arguments

Optional Arguments

You can also pass Optional arguments to a Function.  In other words, sometimes you may need the argument, and sometimes you may not – depending on what code you are using the Function with .

Function CalculateDayDiff(Date1 as Date, Optional Date2 as Date) as Double
'check for second date and if not there, make Date2 equal to today's date.
   If Date2=0 then Date2 = Date
'calculate difference
   CalculateDayDiff = Date2-Date1 
End Function

vba function optional parameter

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!

automacro

Learn More

Default Argument Value

You can also set the default value of the Optional arguments when you are creating the function so that if the user omits the argument, the value that you have put as default will be used instead.

Function CalculateDayDiff(Date1 as Date, Optional Date2 as Date="06/02/2020") as Double 
'calculate difference 
   CalculateDayDiff = Date2-Date1 
End Function

vba functions optional default

ByVal and ByRef

When you pass values to a function, you can use the ByVal or ByRef keywords.  If you omit either of these, the ByRef is used as the default.

ByVal means that you are passing a copy of the variable to the function, whereas ByRef means you are referring to the original value of the variable.  When you pass a  copy of the variable (ByVal), the original value of the variable is NOT changed, but when you reference the variable, the original value of the variable is changed by the function.

Function GetValue(ByRef intA As Integer) As Integer
   intA = intA * 4
   GetValue = intA
End Function

In the function above, the ByRef could be omitted and the function would work the same way.

Function GetValue(intA As Integer) As Integer
   intA = intA * 4
   GetValue = intA
End Function

To call this function, we can run a sub-procedure.

Sub TestValues()
   Dim intVal As Integer
'populate the variable with the value 10
   intVal = 10
'run the GetValue function, and show the value in the immediate window
   Debug.Print GetValue(intVal)
'show the value of the intVal variable in the immediate window 
   Debug.Print  intVal
End Sub

vba function by ref

Note that the debug windows show the value 40 both times.  When you pass the variable IntVal to the function – the value of 10 is passed to the function, and multiplied by 4.  Using the ByRef keyword (or omitting it altogether), will AMEND the value of the IntVal variable.   This is shown when you show first the result of the function in the immediate window (40), and then the value of the IntVal variable in the debug window (also 40).

If we do NOT want to change the value of the original variable, we have to use ByVal in the function.

Function GetValue(ByVal intA As Integer) As Integer
intA = intA * 4
GetValue = intA
End Function

Now if we call the function from a sub-procedure, the value of the variable IntVal will remain at 10.

vba function byval

Exit Function

If you create a function that tests for a certain condition, and once the condition is found to be true, you want return the value from the function, you may need to add an Exit Function statement in your Function in order to exit the function before you have run through all the code in that function.

Function FindNumber(strSearch As String) As Integer
   Dim i As Integer
'loop through each letter in the string
   For i = 1 To Len(strSearch)
   'if the letter is numeric, return the value to the function
      If IsNumeric(Mid(strSearch, i, 1)) Then
         FindNumber= Mid(strSearch, i, 1)
   'then exit the function
         Exit Function
      End If
   Next
   FindNumber= 0
End Function

The function above will loop through the string that is provided until it finds a number, and then return that number from the string.  It will only find the first number in the string as it will then Exit the function.

The function above can be called by a Sub routine such as the one below.

Sub CheckForNumber()
   Dim NumIs as Integer
'pass a text string to the find number function
   NumIs = FindNumber("Upper Floor, 8 Oak Lane, Texas")
'show the result in the immediate window
   Debug.Print NumIs
End Sub

vba function exit function

VBA Programming | Code Generator does work for you!

Using a Function from within an Excel Sheet

In addition to calling a function from your VBA code using a sub procedure, you can also call the function from within your Excel sheet.  The functions that you have created should by default appear in your function list in the User Defined section of the function list.

Click on the fx to show the Insert Function dialog box.

vba function fx

Select User Defined from the Category List

vba function udf

Select the function you require from the available User Defined Functions (UDF’s).

vba function excel sheet

Alternatively, when you start writing your function in Excel, the function should appear in the drop down list of functions.

vba function dropdown

If you do not want the function to be available inside an Excel sheet, you need to put the Private word in front of the word Function when you create the function in your VBA code.

Private Function CalculateDayDiff(Date1 as Date, Date2 as Date) as Double 
   CalculateDayDiff = Date2-Date1 
End Function

It will now not appear in the drop down list showing the Excel functions available.

vba function dropdown 2

Interestingly enough, however, you can still use the function – it just will not appear in the list when looking for it!

vba function excel

If you have declared the second argument as Optional, you can omit it within the Excel sheet as well as within the VBA code.

vba function excel 2

You can also use the a function that you have created without arguments in your Excel sheet.

vba function no argument excel

I’ve tried to attempt something that was answered by JaredPar ByRef vs ByVal Clarification

ByVal in VB.NET means that a copy of the provided value will be sent
to the function. For value types (Integer, Single, etc.) this will
provide a shallow copy of the value. With larger types this can be
inefficient. For reference types though (String, class instances) a
copy of the reference is passed. Because a copy is passed in mutations
to the parameter via = it won’t be visible to the calling function.

ByRef in VB.NET means that a reference to the original value will be
sent to the function (1). It’s almost like the original value is being
directly used within the function. Operations like = will affect the
original value and be immediately visible in the calling function.

And I’ve tried to test it with the following code and I can’t seem to get it to work use the ByRef to change the value of the cell to 0 if it’s 1

Here’s my below code that I’m testing with, what am I doing wrong? The value of Range("A1") is still 1

Sub test1()

    Dim trythis As Boolean

    trythis = False

    If (Sheets("TESTING").Range("A1").value = 1) Then
        testingRoutine (trythis)
        If (trythis) Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean)

    Debug.Print "Ran Function"
    trythis = True

End Function

Martijn Pieters's user avatar

asked Oct 26, 2017 at 17:00

Maldred's user avatar

6

VB subroutines don’t require braces around the argument list. However, if you pass a single argument and you enclose that in braces, you are passing an expression . Expressions cannot be passed by reference. They are evaluated and their result is passed. Therefore you must remove the braces in the call testingRoutine (trythis) and write testingRoutine trythis

Note: if you call a function without using its return value, it must be written as a procedure call (without braces around the argument list). As an example:

myVal = myFunction (trythis)   ' trythis will be passed by reference
myFunction (trythis)           ' trythis will be seen as an expression
myFunction trythis             ' trythis will be passed by reference

myVal = mySub (trythis)        ' invalid: mySub is not a function
mySub (trythis)                ' trythis will be seen as an expression
mySub trythis                  ' trythis will be passed by reference

Of course, the problem will be clear immediately when a function or sub has more than one parameter because a comma cannot appear in an expression.

answered Oct 26, 2017 at 17:12

Paul Ogilvie's user avatar

Paul OgilviePaul Ogilvie

25k4 gold badges21 silver badges41 bronze badges

2

Change this:

testingRoutine (trythis)

to this:

testingRoutine trythis

then try it.

Also, look what happens if I change it to this, where the function is being used as a function (returning something)

Sub test1()

    Dim trythis As Boolean
    Dim testit As Boolean

    trythis = False

    If (Sheets("Sheet1").Range("A1").Value = 1) Then
        testit = testingRoutine(trythis)
        If (trythis) Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").Value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean) As Boolean

    Debug.Print "Ran Function"
    trythis = True

End Function

answered Oct 26, 2017 at 17:08

braX's user avatar

braXbraX

11.5k5 gold badges20 silver badges33 bronze badges

4

I would do it this way.

Sub test1()

    Dim trythis As Boolean

    trythis = False

    If (Sheets("TESTING").Range("A1").value = 1) Then
        tr = testingRoutine(trythis)
        If tr Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean)

    Debug.Print "Ran Function"
    testingRoutine = True

End Function

Because trythis is not a global variable, changing it in the function will do nothing in the sub. It will just define trythis as True in the function and stay in that scope. To get trythis to be affected by the function and read by the sub you have to assign it to a variable within the sub.

answered Oct 26, 2017 at 17:13

n8.'s user avatar

n8.n8.

1,7313 gold badges16 silver badges38 bronze badges

i can’t remember where i read this, but i know that if you add the bracets () to arguments when calling a sub/function, it is a specific (not an error) way microsoft implented to workaround byref calls (to byval).

example : lets say you call a sub who has ONLY byref, and 2 arguments:

call Sub1 (a), (b)

is a proper way to force Byvalarguments, even if they were initially coded as Byval

answered Oct 26, 2017 at 21:30

Patrick Lepelletier's user avatar

1

Понравилась статья? Поделить с друзьями:
  • Byte word and dword
  • Calculate percentage for excel
  • Byte to word delphi
  • Calculate percent in excel
  • Byte to word convert