Why does my code run without 'Option Explicit' but fail with it? - excel

I am constructing my first major program in VBA (With some help from SO). The beginning of it is below, and I am adding blocks of code in as structured a way as possible. Having read about the importance of using 'Option Explicit' though, I inserted it and started to declare all my variables. Running the program gave - 'Compile Error: Variable not Defined'.
I have tried deleting and re-entering declarations in case the problem was caused by me adding 'Option Explicit' after starting on the code, as well as removing the UDF and adding that back in, but without success.
Commenting out 'Option Explicit' does away with the error message and the subsequent blocks of code run exactly as designed.
EDIT:I should have said that the error always occurs with 'iLoopControl'.
Option Explicit
'UDF to roll a number of Dice of specified size and total them
Function Dice(NoOfDice, DiceSize)
Dice = 0
For iLoopControl = 1 To NoOfDice
Dice = Dice + WorksheetFunction.RandBetween(1, DiceSize)
Next
End Function
Sub MercOne()
Randomize
Dim Merc(86)
Dim Temp As Integer, TechLevel As Byte, ArmOfService As Byte
Dim Year As Byte, YearCount As Byte
Dim GenAssignment As Variant, UnitAssignment As Variant
Dim SpecAssignmentSwitchEnd As Byte, Roll As Byte
Dim Rank As Variant, NoOfDice As Variant, DiceSize As Byte
Dim GenAssignmentSwitchInt As Byte, GenAssignmentSwitchOff As Byte
Dim CharacterNumber As Long, iLoopControl As Long

I applaud you for using Option Explicit! Now you have to learn to use it well...
You have to declare a variable before using it. You use iLoopControl in your function Dice before declaring it. VBA will assume (absent Option Explicit) that you were just lazy, and create a Variant variable for you. But if you misspelled the variable name, it would create a new one - and debugging would be really difficult because you won't understand why values are wrong...
As an aside, it is a good idea to avoid Variant in general, except when you know the variable can contain different types, when you will use it in a For Each loop, or when it is an optional parameter in a function. It is slower than a "regularly typed" variable and takes more space. In larger programs that can make a difference. And if you do want a Variant, you don't need to say As Variant since that is implied.
One more point about your code: you declare iLoopControl in the scope of a Sub. That means it is unknown (and "undeclared") when you are not in that sub. Not even when you are inside a function called from that sub. The only variables that can be "seen" inside a Sub/Function are the ones that are declared at the module level (outside of sub/function), or within the sub itself.

Option Explicit means you must explicitly declare all variables and their types before using them. If you don't include the Option Explicit declaration, then VBA will just create Variant type variables whenever it encounters a new identifier.
So for example, you need to include Dim iLoopControl as Integer if you have declared Option Explicit:
Option Explicit
Dim iLoopControl As Integer
For iLoopControl = 1 To NoOfDice
Dice = Dice + WorksheetFunction.RandBetween(1, DiceSize)
Next
If you didn't include Option Explicit then you could just use iLoopControl without using the Dim iLoopControl as Integer to declare it.

You need to declare variables before using it
Where's End Sub for MercOne()?

Related

As String vs As String() in VBA [duplicate]

This may sound trivial, but what is the difference between
Dim v As String()
and
Dim v() As String
in VB.NET?
No difference. From the VB.NET Language Specification on Arrays:
Array types are specified by adding a modifier to an existing type name. The modifier consists of a left parenthesis, a set of zero or more commas, and a right parenthesis.
...
A variable may also be declared to be of an array type by putting an array type modifier or an array initialization modifier on the variable name. In that case, the array element type is the type given in the declaration, and the array dimensions are determined by the variable name modifier. For clarity, it is not valid to have an array type modifier on both a variable name and a type name in the same declaration.
Originally, in Basic, you had to define arrays, but not variables. And the types of variables were defined by a suffix character: A$ was a string, while A% was an integer and A# was double precision. (and all three were distinct and could be used at the same time) (For single-precision, you could use A!, but that was the default if you just used A)
Eventually, programmers came to realize that those were incredibly bad design choices.
To rectify this, Microsoft added "Option Explicit" which required you to predefine every variable. To lessen the effect on the language, they hijack the "DIM" command, which was used to define arrays, to define scalar variables as well.
So originally:
DIM A(50) ' define 51-element single-precision array
Then
DIM A(50) ' define 51-element single-precision array
DIM A$ ' define a string
Then to get rid of the suffixes, they added the "As {type} syntax"
DIM A(50) ' define 51-element single-precision array
DIM B as String
DIM C(50) as String ' define 51-element string array.
Then they made array size variable.
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
This left a conflict in definition style, so they allowed both:
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
DIM D as String() ' define string array.
There is no difference.
Both Dim v As String() and Dim v() As String will create a string array
Traditionally, in Basic, you would put the parenthesis after the variable name. In VB.Net it is allowed to put them after the type instead if you wish. The result is the same so there is no difference using either syntax. The reason for this addition though is because how you can constuct an array. Consider the following code:
Public Sub MethodThatExpectsAnArray(ByVal arr() As String)
'...
End Sub
Public Sub Main()
Me.MethodThatExpectsAnArray(New String() {"Hello", "World"})
End Sub
In the call I construct the array "on the fly" without any assignment except directly to the method argument. Since there are no variable here I must set the paranthesis after the type. To allow this syntax Microsoft had the choice to either change how you traditionally declare arrays in Basic or allow for both syntaxes. They of course opted for the latter.
There is no difference.
It's mostly semantics. The first reads as create variable "v" of type string array and the 2nd reads as create array "v" of type string. Either way the result is the same array of strings.
There is no difference in the meaning of the two.
If you like to declare several variables in one dim statement, the second form provides more flexibility:
dim v(),v2 as string allows you to declare array types and non array types in the same statement.

Pass ListObject to array. type variable String error

The idea is passing the complete content of a listobject.databodyrange to an array to make operations in memory and not having to access the sheets cells values repeatedly which is very time consuming.
this is the code.
Dim theArray As Variant
theArray = mylistObject.DataBodyRange.value
MsgBox (theArray(1, 1)) '= column 1 row 1 = first element
It works, so far so good.
but!!! since theArray is dimensioned as Variant, their elements are NOT strings, So when passing every of the values of theArray into a function that requires a string an error appears.
what to do?
Note: I know I might change the data type of the function itself to variant, but this function is called from so many routines that i dont dare to touch it. I rather prefer try to look for the way to transform the content of that variant into a string
like theArray(i,j) to str(thearray(i,j)) (which does not work)
some help, some ideas?
EDIT 1:
this is the line of the error:
Dim theclaims As Variant
theclaims = rawClsTbl.DataBodyRange.value
For i = LBound(theclaims, 1) To UBound(theclaims, 1)
myText = deleteRefSigns(theclaims(i, 2))
etc
error: byref argument type missmatch
where:
Function deleteRefSigns(txT As String) As String
i will be trying the solutions proposed.
thx
Related questions:
I asked in overflow myself this question some time ago:
Passing Listobject Range to array and getting error "out of range"
and read also this one
Excel VBA Type Mismatch Error passing range to array
and several others.
The following should work:
Dim MyStr As String
MyStr = CStr(TheArray(1, 1))
Note: Always declare it as a forced array not just as Variant …
Dim TheArray() As Variant 'Variant array (can only be an array)
Dim TheArray As Variant 'Variant (can be a value or array)
… to ensure it contains an array. Otherwise it will contain only a value if you do
TheArray = Range("A1").Value
which might easily fail if your range is dynamic.
If you read a range into an array like
Dim TheArray() As Variant
TheArray = Range("A1:C20").Value
then there is no possibility to declare the array as String it is forced to be Variant by design.
It looks like you don't want deleteRefSigns to modify the argument passed by the calling procedure. If so, you can pass the argument by value...
Function deleteRefSigns(ByVal txT As String) As String

ByRef Argument Type Mismatch Excel VBA

I am using windows 7, Excel 2010, VBA. I am getting an error
"ByRef Argument Type Mismatch". I am assuming it is a problem with my variable type. I found lots of questions like mine but I can't find anything that has helped me figure out my problem.
Variable Declarations
'Force explicit variable declaration
Option Explicit
Private dptData(8) As String
Private TSdata(8) As String
Private fiscalYear(8) As String
Calling Function
parseUserData fiscalYear, dptData, TSdata
Called function Prototype
Function parseUserData(fiscalYear As String, dptDataAs String, TSdata As String)
You're passing an array to a String. Change the function's signature to accept a Variant instead.
Public Function parseUserData(fiscalYear As Variant, dptDataAs Variant, TSdata As Variant)
Debug.Assert IsArray(fiscalYear) And IsArray(dptDataAs) And IsArray(TSdata)
A String parameter can only ever accept a String argument1. Variant on the other hand, can accept anything - including an array - but then you'll want to Assert that you're dealing with an array, so that you halt execution (and prevent a bug) if that's not the case.
Why use a Variant over a typed array?
Using a typed array would work, but a typed array can't be coerced from a Variant parameter - which means this:
Public Sub DoSomething(ByRef args() As String)
...can't be invoked with this otherwise perfectly valid array of strings:
DoSomething Array("string1", "string2", "string3") ' can't pass a variant array!
Changing the signature to DoSomething(ByRef args As Variant) makes it work. All you need to do is to use a meaningful, descriptive, pluralized name to your variant array parameters, so that when IntelliSense is shown when you invoke that procedure, the name tells you everything you need to know.
But... 'Variant' is evil!
No different than many other languages, type safety in VBA is essentially smokes and mirrors. Variant is a very powerful tool and does have its uses - avoid it when you can doesn't mean unlearn its existence. Using it to pass array references around between procedures doesn't hurt the code's readability, maintainability, or stability. Variant enables duck typing and late-binding, and is a legitimate COM type.
It's a hammer. Just make sure not everything becomes a nail, and you'll do great.
1VBA will implicitly convert other value types to a String, but an array can't be coerced into a string, implicitly or explicitly.
It seems you wanted a string array after all but it may be worth mentioning that you can declare a fixed width string var.
Dim dptData As String * 8
dptData = "abc"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abc |
dptData = "abcdefghijk"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abcdefgh|

VBA Multiple Optional Function Parameters

I'm trying to write the SWITCH function in VBA for my coworker who has Excel 2013. I feel that my VBA is strong enough to code this function once I set up all my function parameters. However, I'm not sure how to have an unlimited number of optional parameters in a function (similar to *args in Python). How can I set up a function so that it may have an unlimited number of optional arguments?
You need to use ParamArray, e.g.
Public Function TestSum(ParamArray a())
Dim i As Long
For i = LBound(a) To UBound(a)
TestSum = TestSum + a(i)
Next i
End Function
An interesting question, here's my attempt at replicating the Switch functionality.
You will need to use a ParamArray argument:
Optional. Used only as the last argument in arglist to indicate that the final argument is an Optional array of Variant elements. The ParamArray keyword allows you to provide an arbitrary number of arguments. It may not be used with ByVal, ByRef, or Optional. (source)
Revised, thanks to the comments with #TinMan, we no longer use a Dictionary so this will be compatible with Mac OS without further tweaks.
Function FSwitch2(ValueToMatch As Variant, ParamArray ValuesToMatchAndReturn())
' example of replicating the Switch function available in Office 365, etc.
' https://support.office.com/en-us/article/switch-function-47ab33c0-28ce-4530-8a45-d532ec4aa25e
Dim i As Integer
Dim retVal As Variant
Dim default As Variant
If (UBound(ValuesToMatchAndReturn) + 1) Mod 2 <> 0 Then
' if the array is not evenly sized, assume the last argument is the default value.
default = ValuesToMatchAndReturn(UBound(ValuesToMatchAndReturn))
Else
' Otherwise, default to #N/A error if no match.
default = CVErr(2042)
End If
For i = LBound(ValuesToMatchAndReturn) To UBound(ValuesToMatchAndReturn) Step 2
If ValueToMatch = ValuesToMatchAndReturn(i) Then
retVal = ValuesToMatchAndReturn(i + 1)
Exit For
End If
Next
FSwitch2 = IIf(IsEmpty(retVal), default, retVal)
End Function

Calling a variable by a variable excel

I wondering how I can use a variable to call another variable. For example Apple1, Apple2, Apple3, Apple4, Apple5, Apple6 and AppleNum.
Let us say AppleNum is 4. How can I use AppleNum to call for the code to use Apple4?
Also, What if Apple1, Apple2...ect are objects?
My first thought on how to solve this was by using some sort of Array?
Note: Using Select Case here will work but doesn't simply the code I have and would require each case to be written out individually (a lot of work)
Just out of interest, ts there a way to define AppleNum number of variables in the code?
If you can help, Thanks!
It is possible to have an array of variable size in VBA, using the following (for example):
Dim myArray() as Double
Dim AppleNum as Integer, arraySize as Integer
AppleNum = 4
arraySize = 6
ReDim myArray(1 to arraySize)
this would create an array myArray, and change its size to be 6 elements long (with index starting at 1). You can declare the entire array in one statement (without redimensioning) with
myArray = Array(Apple1, Apple2, Apple3, Apple4, Apple5, Apple6)
Assuming that Apple1 etc. have been previously declared.
If you declare myArray as Variant rather than Integer (or don't give a type - defaults to Variant) then your array can contain anything you want.
You can also use
ReDim Preserve myArray(1 to arraySize * 2)
if at a later point you want the array to be twice as large, but you don't want to lose the first six elements you had assigned.
I hope these things will get you moving with your problem.

Resources