Array variable is used before assigned a value - arrays

When I rebuild the solution I get the warning:
"Variable 'aMyArray' is used before it has been assigned a value."
A function in VB.NET uses an array which is dynamically populated.
Example:
Function MyArray()
Try
Dim aMyArray()
For i = 0 to 100
ReDim Preserve aMyArray(i)
Next
Catch ex As Exception
End Try
End Function
How should I declare a dynamically populated array to eliminate this warning?

I like to use the CreateInstance to avoid possible errors:
Dim aMyArray() as string = Array.CreateInstance(GetType(String),0)

A. Your function returns nothing, so you should also have a warning about that
B. You seriously need to turn on Option Strict and let the compiler point out other errors like aMyArray has no Type.
C. Never use and empty Try/Catch; if there is an exception, you want to know when something goes wrong (and where) so you can fix it.
D. Avoid arrays like the plague. ReDim Preserve aMyArray(i) creates a new array and then copies all the data to the new structure. This can be costly in terms of performance if it is a large array of something like strings. Net has several very nice collections such as List(of T) which do not need to be explicitly resized:
Private myList As New List(of String) ' or Integer, Decimal or even MyClassObject
...
myList.Add("Hello")
myList.Add("Lists...")
myList.Add("Goodbye")
myList.Add("Arrays")
D(1). The result of using a List would mean that entire procedure can be done away with. Just add new things to the list as needed.
E. The code posted cant actually result in the warning because you dont ever add a value to it. Adding: aMyArray(2) = 2 after the loop will result in the warning. This is because you have never fully declared the array (size and type) as in:
Dim aMyArray(100) As String
ReDim simply resizes the array which isnt the same thing.
F. Since there is nothing in your new array, there is no reason to use ReDim Preserve because there is nothing to preserve. I'll assume that is supposed to be some calculation. On the other hand, if you are trying to resize an existing array, it should be a Sub.
We are all left to guess if it really is meant to modify an existing array (based on ReDim Preserve) or return a new array of stuff (considering the array is declared in the procedure).
Again, none of this is needed using a List, Dictionary or other Collection Type.

Related

Excel VBA: Passing Array between subs [duplicate]

VBA question. I'm using it to make Solidworks macros, but that's not important.
Can someone explain the syntax to pass an array (1-Dimensional of type Double, with length of three) to a subroutine or function so I can modify the contents. I know that they have to be passed by reference. I'm talking exact syntax, because everything I try I get type mismatch error on the subroutine/function call.
All I'm looking for is the correct Dim statement for the array of Doubles, initialize statement to make all array values zero, then the subroutine call statement and subroutine header that I would need. Please be specific if I need to use variant type or dynamic array, even when I already know the type and size of the array. Use a function or a sub, I don't care which.
My code works fine as of now, but I'm tired of avoiding function and subroutine calls when I'm using arrays. I've looked at countless documentation and similar questions on here, but I just cannot figure it out. Thanks a lot.
This is fairly trivial, using the ByRef keyword in the function signature will pass your array by reference rather than by value. This means that manipulations to that array will be preserved and bubble up to the calling scope. This is probably an oversimplification, but think of it this way:
ByRef: you're passing a reference to the thing (array, Object, etc.) and any transformations to that thing will be apparent anywhere that thing exists.
ByVal: you're passing the value representation of the thing. Essentially, you're passing a "copy" of it. You can manipulate this object, but it is not the same object from the calling scope, it's just a copy. So when the enclosing scope ends, you're still left with only the original thing.
Initialize your array as a numeric type and that will give you default 0 values.
Example as follows:
Option Explicit
Sub foo()
Dim myArray(1 To 3) As Double 'The array is initialized with zero-values
Call bar(myArray)
MsgBox myArray(3)
End Sub
Sub bar(ByRef dblArray() As Double)
dblArray(1) = 123
dblArray(2) = 345
dblArray(3) = 33456
End Sub
By default, arrays are passed by reference. I've seen in a few places that passing "ByVal" throws an exception. However, I have been able to use ByVal without issue in O365.

VBA Pass Array By Reference and Modify Contents

VBA question. I'm using it to make Solidworks macros, but that's not important.
Can someone explain the syntax to pass an array (1-Dimensional of type Double, with length of three) to a subroutine or function so I can modify the contents. I know that they have to be passed by reference. I'm talking exact syntax, because everything I try I get type mismatch error on the subroutine/function call.
All I'm looking for is the correct Dim statement for the array of Doubles, initialize statement to make all array values zero, then the subroutine call statement and subroutine header that I would need. Please be specific if I need to use variant type or dynamic array, even when I already know the type and size of the array. Use a function or a sub, I don't care which.
My code works fine as of now, but I'm tired of avoiding function and subroutine calls when I'm using arrays. I've looked at countless documentation and similar questions on here, but I just cannot figure it out. Thanks a lot.
This is fairly trivial, using the ByRef keyword in the function signature will pass your array by reference rather than by value. This means that manipulations to that array will be preserved and bubble up to the calling scope. This is probably an oversimplification, but think of it this way:
ByRef: you're passing a reference to the thing (array, Object, etc.) and any transformations to that thing will be apparent anywhere that thing exists.
ByVal: you're passing the value representation of the thing. Essentially, you're passing a "copy" of it. You can manipulate this object, but it is not the same object from the calling scope, it's just a copy. So when the enclosing scope ends, you're still left with only the original thing.
Initialize your array as a numeric type and that will give you default 0 values.
Example as follows:
Option Explicit
Sub foo()
Dim myArray(1 To 3) As Double 'The array is initialized with zero-values
Call bar(myArray)
MsgBox myArray(3)
End Sub
Sub bar(ByRef dblArray() As Double)
dblArray(1) = 123
dblArray(2) = 345
dblArray(3) = 33456
End Sub
By default, arrays are passed by reference. I've seen in a few places that passing "ByVal" throws an exception. However, I have been able to use ByVal without issue in O365.

Remove an item at a specific Index from a dynamic String Array

I have a dynamic string array and I have populated it with some dynamic values using ReDim etc. I would like to delete an item from the string array at a specific position. I have read about using a Where condition using System.Linq but can't really get a proper answer to this. Any suggestions?
Dim dynamic() as String
...
Redim dynamic(2)
..
dynamic(0) = Apple
dynamic(1) = Banana
dynamic(2) = Orange
..
dynamic(i).Remove... etc?
As others have mentioned, there is technically no way to remove an element from an array. The only way to modify the number of elements in the array is to create a whole new array and copy the contents from the one to the other. There are more convenient methods you can use to do it, but in the end, that's what they are actually doing for you, just behind the scenes.
If you are going to be doing a lot of that kind of thing, I would recommend using the List(Of String) class rather than a String array. As others have pointed out, the List(Of T) class is specifically designed to make those kinds of dynamic functions more simple. If, you ever need the list as an array, you can very easily (and efficiently) convert it to an array like this:
Dim myList As New List(Of String)()
myList.Add("Apple")
myList.Add("Banana")
myList.Add("Orange")
myList.RemoveAt(1)
Dim myArray() As String = myList.ToArray()
However, if you really must have it as an array, LINQ does make it a bit easier. By importing the LINQ namespace, many new LINQ extension methods will be added to arrays (and any other list or enumerable objects). For instance, you could use LINQ's Except method, like this:
Dim dynamic() As String = {"Apple", "Banana", "Orange"}
dynamic = dynamic.Except({"Banana"}).ToArray()
Notice that the Except method returns an enumerable object, but not an array. To convert it to an array, you'll need to additionally call LINQ's ToArray method, as in the above example. That above example removes the string "Banana" from the middle of the array. If, however, you don't know the value and you just want to remove it by index, LINQ doesn't provide a way to filter by index like that. You could still use the Except method like this, though:
Dim dynamic() As String = {"Apple", "Banana", "Orange"}
dynamic = dynamic.Except({dynamic(1)}).ToArray()
You may be wondering why I put the curly brackets around the parameter passed to the Except method. I did that because the Except method actually takes a list of parameters (IEnumerable, to be specific). Since array's implement IEnumerable (as well as IList, by the way), I just wrapped the parameter in an array before passing it to the method. The interesting upshot of this is that if you have multiple items to remove, you could do them all in one command, like this:
Dim dynamic() As String = {"Apple", "Banana", "Orange"}
dynamic = dynamic.Except({"Apple", "Banana"}).ToArray()
You can't do it directly with arrays. But you can do this:
Dim tmp = New List(Of string)(dynamic)
tmp.RemoveAt(index)
dynamic = tmp.ToArray()

VBA Excel - Returning an array of ListBoxes from a function

I am currently working on a module that is meant to populate three ActiveX ListBoxes. Each will be populated with the same values: choices for axes titles on a graph (X-axis, Primary Y-Axis and Secondary Y-Axis). I have a function called "FillBox" that takes a ListBox as an argument and populates it.
Originally I used the following to accomplish this:
Sub FillAllBoxes()
FillBox X_BOX
FillBox Y1_BOX
FillBox Y2_BOX
End Sub
I was informed that I cannot use a Union to compact this code because of the MSForms.ListBox object type. There are many other subs that perform a repeated operation on each box, so it appears that some sort of array would be best to use.
So far, the only way I have found to define this array of ListBoxes is the following, though I am unsatisfied with the fact that the return value is a Variant but I can't seem to get it to work if I define it as Function BOX_ARRAY() As MSForms.ListBox:
Function BOX_ARRAY() As Variant
Dim Temp(1 To 3) As MSForms.ListBox
Set Temp(1) = X_BOX
Set Temp(2) = Y1_BOX
Set Temp(3) = Y2_BOX
BOX_ARRAY = THIS_ARRAY
End Function
I can then use this sub, though I needed to explicitly define the argument to FillBox as ByVal, because BOX_ARRAY is a variant.
Sub FillAllBoxes()
For j = LBound(BOX_ARRAY) To UBound(BOX_ARRAY)
FillBox BOX_ARRAY(j)
Next j
End Sub
A) Is there any way to have a function return an array that is not a Variant?
B) Would it be wise to stick with my original method of just repeating the function explicitly for each list box or is it worth using arrays for this type of operation?
C) In the function BOX_ARRAY is there a way around defining a Temp array and just populating the returned array right off the bat?
Here goes:
There are many other subs that perform a repeated operation on each box
Simply pass the ListBox to a function that operates on listboxes, e.g.:
Sub MyExample()
EnableListBoxes Array(Me.ListBox1, Me.ListBox2, Me.ListBox3)
End Sub
Sub EnableListBoxes(boxes as Variant)
Dim lbox as variant
For each lbox in boxes
lbox.Enabled = True
Next
End Sub
Alternatively you can pass the entire form/worksheet/whatever contains the listboxes, and iterate over the collection containing them (e.g., UserForm.Controls, etc.) You'd need to do some additional logic to make sure you're operating on the right type of form control, but that is relatively easy to do.
I can't seem to get it to work if I define it as Function BOX_ARRAY() As MSForms.ListBox
Of course not, because an MSForms.ListBox is a single object. You can't expect to return an array of anything in to a function that is designed to return an object.
Is there any way to have a function return an array that is not a Variant?
Perhaps use a subroutine and pass the arguments ByRef. You don't need to return a value if you are operating on the reference directly, rather than a local copy of it. See Chip Pearson for some detailed explanations/examples:
http://www.cpearson.com/excel/passingandreturningarrays.htm
Sub TestByRef()
Dim i as Integer
i = 1
ChangeValue i
Debug.Print i
End Sub
Sub ChangeValue(ByRef x as Integer)
x = 3
End Sub
In the function BOX_ARRAY is there a way around defining a Temp array and just populating the returned array right off the bat?
Not that I can think of. Perhaps with a class module and the class _Initialize method, you could automatically assign one of the class properties. But I think that ultimately is the same method: creating a temporary array that defines the return value. It is hard to tell for sure since you don't provide enough code (e.g., where are the variables? are they even declared?

ReDim an Array Pointer VB6

I'm trying to ReDim a member object array from a different class. For example:
Class1.cls
Dim mStuffArray() As New clsStuff
Property Get StuffArray() As clsStuff()
StuffArray = mStuffArray
End Property
Class2.cls
Private Sub Foo(ByRef pClass1 As Class1)
Dim tStuffArray() As clsStuff
tStuffArray = pClass1.StuffArray
ReDim tStuffArray(1 To 2)
End Private
The problem here is that Foo doesn't seem to be ReDim'ing the member mStuffArray in Class1. Any idea what I'm doing wrong? Forgive me if my VB6 looks odd or the naming conventions aren't standard, I had to dive into some old legacy code and am new to VB6
Dave
Redim doesn't make a copy of the array.
I think it's more likely that 4eturning the array from a property get creates a copy. The docs aren't very clear. http://msdn.microsoft.com/en-us/library/aa261343(VS.60).aspx
It would be simpler to use a Public member variable. And why not use a Collection rather than an array?
I've never looked into VB6, but if I were to take a guess, I think when you use ReDim, it creates a copy of the existing Array and changes tStuffArray to point to the new copy. However, pClass1.mStuffArray still references the old array.
The documentation for ReDim states that "ReDim creates a new array, copying all the elements from the existing array"
I would recommend adding a method to to Class1 that performs ReDim on the private mStuffArray variable.
Dim mStuffArray() As New clsStuff
Property Get StuffArray() As clsStuff()
StuffArray = mStuffArray
End Property
Sub Foo()
ReDim mStuffArray(1 To 2)
End Sub
Hopefully this works. As I said, I'm not a VB6 programmer, so I may be off on this.
You might also want to consider the Dictionary object.

Resources