is it possible to pass all elements of an array to a ParamArray?
For example I'd like to pass a ParamArray to another ParamArray:
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys 'should be the same as: p2 "test", "banane", "birne"
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key 'Run-time error '13' Type mismatch (key is an array)
Next key
End Sub
In this case ParamArray of p2 doesn't contain the elements of keys, but it gets the array-object keys. Thus I've got to check, if an arrays is passed:
Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys
End Sub
Sub p2(ParamArray params() As Variant)
Dim keys As Variant
If IsArray(params(0)) Then
keys = params(0)
Else
keys = params
End If
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
But this is awkward for example compared to Java:
public class VarArgs {
public static void main(String[] args) {
p1("test", "banane", "birne");
p2("test", "banane", "birne");
String[] array = {"test", "banane", "birne"};
p1(array);
p2(array);
}
public static void p1(String... strings) {
p2(strings);
}
public static void p2(String... strings) {
for (String string : strings) {
System.out.println(string);
}
}
}
In Java I don't have to distinguish. But this is probably not possible in VBA.
Thanks for help,
Michael
Pass a ParamArray argument to another function that expects a ParamArray argument (delegate ParamArray arguments).
I need to delegate to a function of type: strf(str as string, ParamArray args() as Variant) as String the arguments received in other function in a ParamArray passing directly without explicitly write.
The restrictions I've found are:
A ParamArray() it can only be passed to another function that expects a ParamArray.
The ParamArray is received at element 0 as a Variant ()
When the second function receives it increases a level of depth
I have not found any satisfactory solution, but I have written a function that works perfectly, undoing the depth levels added and returning a vector with arguments received.
Code:
Option Explicit
Option Base 1
Public Sub PrAr1(ParamArray pa1() As Variant)
Dim arr() As Variant
arr = fn.ParamArrayDelegated(pa1)
PrAr2 pa1
End Sub
Public Sub PrAr2(ParamArray pa2() As Variant)
Dim i As Integer, arrPrms() As Variant
arrPrms = fn.ParamArrayDelegated(pa2)
For i = 0 To UBound(arrPrms)
Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
Next i
PrAr3 pa2
End Sub
Public Sub PrAr3(ParamArray pa3() As Variant)
Dim i As Integer, arrPrms() As Variant
arrPrms = fn.ParamArrayDelegated(pa3)
For i = 0 To UBound(arrPrms)
Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
Next i
End Sub
Public Function ParamArrayDelegated(ParamArray prms() As Variant) As Variant
Dim arrPrms() As Variant, arrWrk() As Variant
'When prms(0) is Array, supposed is delegated from another function
arrPrms = prms
Do While VarType(arrPrms(0)) >= vbArray And UBound(arrPrms) < 1
arrWrk = arrPrms(0)
arrPrms = arrWrk
Loop
ParamArrayDelegated = arrPrms
End Function
you could convert it into a Variant from the 2nd call on:
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 CVar(keys) '<--| pass it as a Variant
End Sub
Sub p2(keys As Variant) '<--| accept a Variant argument
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
Here's my solution. Note that Its one restriction is that you can pass only one (Variant) array argument to the ParamArray parameter set. Possibly it could be generalized to handle multiple passed arrays but I have yet to run into that need.
Option Explicit
Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
Dim TempKeys As Variant
TempKeys = keys 'ParamArray isn't actually a standard Variant array, so you have to copy
'it to one in order for the added test/workaround in p2 to not crash
'Excel.
p2 TempKeys 'should be the same as: p2 "test", "banane", "birne"
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
If IsArray(keys(0)) Then keys = keys(0) 'Set this routine's ParamArray parameter to be
'the array of its first element.
For Each key In keys
Debug.Print key
Next key
End Sub
Try:
Sub p2(ParamArray keys() As Variant)
dim myKey as Variant
If IsArray(keys(0)) Then
myKey = keys(0)
Else
myKey = keys()
End If
...
end sub
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key(0) '<- Give an Index here.
Next key
End Sub
One of my strongest needs is to be able to take a ParamArray values() As Variant and turned it into a String().
Per the OPs question, I also need to be able to forward other functions to this function, where the other functions have a ParamArray which needs also to be converted to a String() before that function can continue to process.
Here's the solution which includes a robust function to safely return an Array's size:
Public Function f_uas_astrFromParamArray( _
ParamArray pr_avarValues() As Variant _
) As String()
Dim astrResult() As String
Dim avarTemp() As Variant
Dim lngSize As Long
Dim lngUBound As Long
Dim lngIndex As Long
If (IsMissing(pr_avarValues) = False) Then
If (IsArray(pr_avarValues(0)) = True) Then
avarTemp = pr_avarValues(0)
Else
avarTemp = pr_avarValues
End If
lngSize = f_lngArraySize(avarTemp)
If (lngSize > 0) Then
lngUBound = lngSize - 1
ReDim astrResult(0 To lngUBound)
For lngIndex = 0 To lngUBound
astrResult(lngIndex) = CStr(avarTemp(lngIndex))
Next lngIndex
End If
End If
f_uas_astrFromParamArray = astrResult
End Function
'Return Value:
' -1 - Not an Array
' 0 - Empty
' > 0 - Defined
Public Function f_ua_lngArraySize( _
ByRef pr_avarValues As Variant _
, Optional ByVal pv_lngDimensionOneBased As Long = 1 _
) As Long
Dim lngSize As Long: lngSize = -1 'Default to not an Array
Dim lngLBound As Long
Dim lngUBound As Long
On Error GoTo Recovery
If (IsArray(pr_avarValues) = True) Then
lngSize = 0 'Move default to Empty
lngLBound = LBound(pr_avarValues, pv_lngDimensionOneBased)
lngUBound = UBound(pr_avarValues, pv_lngDimensionOneBased)
If (lngLBound <= lngUBound) Then
lngSize = lngUBound - lngLBound + 1 'Non-Empty, so return size
End If
End If
NormalExit:
f_ua_lngArraySize = lngSize
Exit Function
Recovery:
GoTo NormalExit
End Function
In order to pass a ParamArray Variant from function to function called from Excel itself, the unboxing principle showed in previous posts by #JoséIborraBotia was working for a list of ranges,
but
catching the exception raised when trying to unbox one level to much instead of
testing VarType, UBound or IsArray like proposed previously,
allows it to also work for a single range,
witch is critical when passing Excel selections to a custom function.
Let find this unboxing function along with a demonstration of its use to count any combination of Excel cells selection:
ParamArray unboxing function:
Public Function unboxPA(ParamArray prms() As Variant) As Variant
Dim arrPrms() As Variant, arrWrk() As Variant
Dim done As Boolean
done = False
arrPrms = prms
Do While Not done
On Error Resume Next
arrWrk = arrPrms(0)
If (Err.Number > 0) Then
done = True
End If
arrPrms = arrWrk
Loop
unboxPA = arrPrms
End Function
Unboxing used to count any excel cells selction:
Function MyCountLargeCellsPA(ParamArray rangeArray() As Variant)
Dim unboxed() As Variant
unboxed = unboxPA(rangeArray)
Dim n As Long
For n = LBound(unboxed) To UBound(unboxed)
MyCountLargeCellsPA = MyCountLargeCellsPA + unboxed(n).CountLarge
Next
End Function
This allow any nested call from function to function using ParamArray,
It is now possible to program using VBA !
This will not help the OP anymore since the question has been asked 10 years ago in 2013.
However, if anyone runs into this issue, please do not implement such a horrible stuff blowing up the code logic (and performance) as suggested by most of the answers. Just write clean code to get around that issue.
If you actually want a ParamArray, then do that in the public interface and convert it into an ordinary Array. Let the implementation do the stuff in a straight forward way.
Option Explicit
Public Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
p3 "test", "banane", "birne"
End Sub
'public interface
Public Sub p1(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p1V keys_
End Sub
Public Sub p2(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p2V keys_
End Sub
Public Sub p3(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p3V keys_
End Sub
'private (or even public) implementation
Private Sub p1V(keys() As Variant)
p2V keys
End Sub
Private Sub p2V(keys() As Variant)
p3V keys
End Sub
Private Sub p3V(keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
' "test", "banane", "birne"
' no matter which cascaded function was called
End Sub
paramArrays are weird but you can use normal Array, which works just fine
Sub test()
Dim a As Variant: a = Array("test", "banane", "birne")
p1 a
End Sub
Sub p1(keys As Variant)
p2 keys
End Sub
Sub p2(keys As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
Related
is it possible to pass all elements of an array to a ParamArray?
For example I'd like to pass a ParamArray to another ParamArray:
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys 'should be the same as: p2 "test", "banane", "birne"
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key 'Run-time error '13' Type mismatch (key is an array)
Next key
End Sub
In this case ParamArray of p2 doesn't contain the elements of keys, but it gets the array-object keys. Thus I've got to check, if an arrays is passed:
Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys
End Sub
Sub p2(ParamArray params() As Variant)
Dim keys As Variant
If IsArray(params(0)) Then
keys = params(0)
Else
keys = params
End If
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
But this is awkward for example compared to Java:
public class VarArgs {
public static void main(String[] args) {
p1("test", "banane", "birne");
p2("test", "banane", "birne");
String[] array = {"test", "banane", "birne"};
p1(array);
p2(array);
}
public static void p1(String... strings) {
p2(strings);
}
public static void p2(String... strings) {
for (String string : strings) {
System.out.println(string);
}
}
}
In Java I don't have to distinguish. But this is probably not possible in VBA.
Thanks for help,
Michael
Pass a ParamArray argument to another function that expects a ParamArray argument (delegate ParamArray arguments).
I need to delegate to a function of type: strf(str as string, ParamArray args() as Variant) as String the arguments received in other function in a ParamArray passing directly without explicitly write.
The restrictions I've found are:
A ParamArray() it can only be passed to another function that expects a ParamArray.
The ParamArray is received at element 0 as a Variant ()
When the second function receives it increases a level of depth
I have not found any satisfactory solution, but I have written a function that works perfectly, undoing the depth levels added and returning a vector with arguments received.
Code:
Option Explicit
Option Base 1
Public Sub PrAr1(ParamArray pa1() As Variant)
Dim arr() As Variant
arr = fn.ParamArrayDelegated(pa1)
PrAr2 pa1
End Sub
Public Sub PrAr2(ParamArray pa2() As Variant)
Dim i As Integer, arrPrms() As Variant
arrPrms = fn.ParamArrayDelegated(pa2)
For i = 0 To UBound(arrPrms)
Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
Next i
PrAr3 pa2
End Sub
Public Sub PrAr3(ParamArray pa3() As Variant)
Dim i As Integer, arrPrms() As Variant
arrPrms = fn.ParamArrayDelegated(pa3)
For i = 0 To UBound(arrPrms)
Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
Next i
End Sub
Public Function ParamArrayDelegated(ParamArray prms() As Variant) As Variant
Dim arrPrms() As Variant, arrWrk() As Variant
'When prms(0) is Array, supposed is delegated from another function
arrPrms = prms
Do While VarType(arrPrms(0)) >= vbArray And UBound(arrPrms) < 1
arrWrk = arrPrms(0)
arrPrms = arrWrk
Loop
ParamArrayDelegated = arrPrms
End Function
you could convert it into a Variant from the 2nd call on:
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 CVar(keys) '<--| pass it as a Variant
End Sub
Sub p2(keys As Variant) '<--| accept a Variant argument
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
Here's my solution. Note that Its one restriction is that you can pass only one (Variant) array argument to the ParamArray parameter set. Possibly it could be generalized to handle multiple passed arrays but I have yet to run into that need.
Option Explicit
Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
Dim TempKeys As Variant
TempKeys = keys 'ParamArray isn't actually a standard Variant array, so you have to copy
'it to one in order for the added test/workaround in p2 to not crash
'Excel.
p2 TempKeys 'should be the same as: p2 "test", "banane", "birne"
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
If IsArray(keys(0)) Then keys = keys(0) 'Set this routine's ParamArray parameter to be
'the array of its first element.
For Each key In keys
Debug.Print key
Next key
End Sub
Try:
Sub p2(ParamArray keys() As Variant)
dim myKey as Variant
If IsArray(keys(0)) Then
myKey = keys(0)
Else
myKey = keys()
End If
...
end sub
Sub test()
p1 "test", "banane", "birne"
End Sub
Sub p1(ParamArray keys() As Variant)
p2 keys
End Sub
Sub p2(ParamArray keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key(0) '<- Give an Index here.
Next key
End Sub
One of my strongest needs is to be able to take a ParamArray values() As Variant and turned it into a String().
Per the OPs question, I also need to be able to forward other functions to this function, where the other functions have a ParamArray which needs also to be converted to a String() before that function can continue to process.
Here's the solution which includes a robust function to safely return an Array's size:
Public Function f_uas_astrFromParamArray( _
ParamArray pr_avarValues() As Variant _
) As String()
Dim astrResult() As String
Dim avarTemp() As Variant
Dim lngSize As Long
Dim lngUBound As Long
Dim lngIndex As Long
If (IsMissing(pr_avarValues) = False) Then
If (IsArray(pr_avarValues(0)) = True) Then
avarTemp = pr_avarValues(0)
Else
avarTemp = pr_avarValues
End If
lngSize = f_lngArraySize(avarTemp)
If (lngSize > 0) Then
lngUBound = lngSize - 1
ReDim astrResult(0 To lngUBound)
For lngIndex = 0 To lngUBound
astrResult(lngIndex) = CStr(avarTemp(lngIndex))
Next lngIndex
End If
End If
f_uas_astrFromParamArray = astrResult
End Function
'Return Value:
' -1 - Not an Array
' 0 - Empty
' > 0 - Defined
Public Function f_ua_lngArraySize( _
ByRef pr_avarValues As Variant _
, Optional ByVal pv_lngDimensionOneBased As Long = 1 _
) As Long
Dim lngSize As Long: lngSize = -1 'Default to not an Array
Dim lngLBound As Long
Dim lngUBound As Long
On Error GoTo Recovery
If (IsArray(pr_avarValues) = True) Then
lngSize = 0 'Move default to Empty
lngLBound = LBound(pr_avarValues, pv_lngDimensionOneBased)
lngUBound = UBound(pr_avarValues, pv_lngDimensionOneBased)
If (lngLBound <= lngUBound) Then
lngSize = lngUBound - lngLBound + 1 'Non-Empty, so return size
End If
End If
NormalExit:
f_ua_lngArraySize = lngSize
Exit Function
Recovery:
GoTo NormalExit
End Function
In order to pass a ParamArray Variant from function to function called from Excel itself, the unboxing principle showed in previous posts by #JoséIborraBotia was working for a list of ranges,
but
catching the exception raised when trying to unbox one level to much instead of
testing VarType, UBound or IsArray like proposed previously,
allows it to also work for a single range,
witch is critical when passing Excel selections to a custom function.
Let find this unboxing function along with a demonstration of its use to count any combination of Excel cells selection:
ParamArray unboxing function:
Public Function unboxPA(ParamArray prms() As Variant) As Variant
Dim arrPrms() As Variant, arrWrk() As Variant
Dim done As Boolean
done = False
arrPrms = prms
Do While Not done
On Error Resume Next
arrWrk = arrPrms(0)
If (Err.Number > 0) Then
done = True
End If
arrPrms = arrWrk
Loop
unboxPA = arrPrms
End Function
Unboxing used to count any excel cells selction:
Function MyCountLargeCellsPA(ParamArray rangeArray() As Variant)
Dim unboxed() As Variant
unboxed = unboxPA(rangeArray)
Dim n As Long
For n = LBound(unboxed) To UBound(unboxed)
MyCountLargeCellsPA = MyCountLargeCellsPA + unboxed(n).CountLarge
Next
End Function
This allow any nested call from function to function using ParamArray,
It is now possible to program using VBA !
This will not help the OP anymore since the question has been asked 10 years ago in 2013.
However, if anyone runs into this issue, please do not implement such a horrible stuff blowing up the code logic (and performance) as suggested by most of the answers. Just write clean code to get around that issue.
If you actually want a ParamArray, then do that in the public interface and convert it into an ordinary Array. Let the implementation do the stuff in a straight forward way.
Option Explicit
Public Sub test()
p1 "test", "banane", "birne"
p2 "test", "banane", "birne"
p3 "test", "banane", "birne"
End Sub
'public interface
Public Sub p1(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p1V keys_
End Sub
Public Sub p2(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p2V keys_
End Sub
Public Sub p3(ParamArray keys() As Variant)
Dim keys_() As Variant: keys_ = keys
p3V keys_
End Sub
'private (or even public) implementation
Private Sub p1V(keys() As Variant)
p2V keys
End Sub
Private Sub p2V(keys() As Variant)
p3V keys
End Sub
Private Sub p3V(keys() As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
' "test", "banane", "birne"
' no matter which cascaded function was called
End Sub
paramArrays are weird but you can use normal Array, which works just fine
Sub test()
Dim a As Variant: a = Array("test", "banane", "birne")
p1 a
End Sub
Sub p1(keys As Variant)
p2 keys
End Sub
Sub p2(keys As Variant)
Dim key As Variant
For Each key In keys
Debug.Print key
Next key
End Sub
I have a code like below. This is main logic function and I'd like to insert different procedures in that procedure. So is it a way or solution to do that. I marked with **.
Public Shared Sub CheckListSubstrs(ByVal Substrs As IScrNamedObjectList, **mySub(Substr As IScrSubstructure)**)
Dim Substr As IScrSubstructure = Nothing
Dim nSubstr As Integer = Nothing
nSubstr = Substrs.count
If nSubstr > 0 Then
For i As Integer = 0 To nSubstr - 1
Substr = CType(Substrs.item(i), IScrSubstructure)
**mySub(Substr As IScrSubstructure)**
Next
End If
End Sub
I have different types of sub/func procedures and all of them uses Substr As IScr as Substructure as their input so I'd like to insert them dynamically and call them for different classes, modules.
EDIT
I have to clarify my problem more specific to clear conversations.
This is my class with all values.
Option Explicit On
Option Strict On
Imports simpackcomslvLib
Public Class Substr
Public Shared Sub CheckListSubstrs(ByVal Substrs As IScrNamedObjectList, ByVal dgv As DataGridView, SourceType As ****)
Dim nSubstr As Integer = Nothing
nSubstr = Substrs.count
If nSubstr > 0 Then
For i As Integer = 0 To nSubstr - 1
Dim Substr As IScrSubstructure = CType(Substrs.item(i), IScrSubstructure)
'Procedure comes here according to element type for example listing bodies
' CheckListBodies(Substr.getBodyList(False), DataGridView2)
'or if i list forces
'CheckListForces(Substr.getForceList(False), DataGridView3)
'Recursive usage function to get lower substructures information you can think there's a cascaded structure of substructures
CheckListSubstrs(Substrs:=Substr.getSubstrList(False), ProcedureForElementType As ****)
Next
End If
End Sub
Private Shared Sub CheckListBodies(ByVal Bodies As IScrNamedObjectList, ByVal dgv As DataGridView)
Dim nBody As Integer
nBody = Bodies.count
For i As Integer = 0 To nBody - 1
Dim Body As IScrBody = CType(Bodies.item(i), IScrBody)
dgv.Rows.Add(Body.fullName)
Next
End Sub
Private Shared Sub CheckListForces(ByVal Forces As IScrNamedObjectList, ByVal dgv As DataGridView)
Dim nForce As Integer
nForce = Forces.count
For i As Integer = 0 To nForce - 1
Dim Force As IScrForce = CType(Forces.item(i), IScrForce)
dgv.Rows.Add(Force.fullName)
Next
End Sub
Public Shared Sub RunTheCodeforBodies()
CheckListSubstrs(Mdl.getSubstrList(False), DataGridView2, getBodyList)
End Sub
Public Shared Sub RunTheCodeforForces()
CheckListSubstrs(Mdl.getSubstrList(False), DataGridView3, getForceList)
End Sub
End Class
As I showed two examples here, I'm listing different types approx. 30 types. I'm using com-interface and this Iscr types of classes from 3rd part software which I'm connecting.
So all of properties belongs to substructures and I only want to change function element type and output datagridview.
Since you already have existing methods that should elaborate a IScrSubstructure object and, as you say, all methods heve the same signature, you can use a method delegate with that same signature and use it as a parameter of the CheckListSubstrs sub.
A simulation, with some objects that can be used for testing:
Public Structure IScrSubstructure
Public value1 As String
Public value2 As Integer
End Structure
Public Class IScrNamedObjectList
Inherits List(Of IScrSubstructure)
End Class
Public Delegate Sub ScrSubstructureDelegate(ByVal Substr As IScrSubstructure)
Public Shared Sub CheckListSubstrs(ByVal Substrs As IScrNamedObjectList, MyDelegate As ScrSubstructureDelegate)
If Substrs?.Count > 0 Then
For Each item As IScrSubstructure In Substrs
MyDelegate(item)
Next
End If
End Sub
Now, your CheckListSubstrs method has a parameter:
MyDelegate As ScrSubstructureDelegate
you can pass any method that matches that signature:
ByVal Substr As IScrSubstructure
If you try to pass a method that doesn't match the delegate signature, the code will not compile.
So, lets build a couple of methods with these characteristics and call the CheckListSubstrs method using both methods as the MyDelegate parameter:
Public Sub MyIScrSub(ByVal Substr1 As IScrSubstructure)
'Do something with Substr1
Console.WriteLine("MyIScrSub Value1: {0}, MyIScrSub Value2: {1}", Substr1.value1, Substr1.value2)
End Sub
Public Sub MyOtherIScrSub(ByVal AnotherSubscr As IScrSubstructure)
'Do something with AnotherSubscr
Console.WriteLine("MyOtherIScrSub Value1: {0}, MyOtherIScrSub Value2: {1}", AnotherSubscr.value1, AnotherSubscr.value2)
End Sub
Now you can call CheckListSubstrs passing both MyIScrSub and MyOtherIScrSub methods as delegate:
Dim ScrList As IScrNamedObjectList = New IScrNamedObjectList()
ScrList.Add(New IScrSubstructure() With {.value1 = "Value1", .value2 = 1})
ScrList.Add(New IScrSubstructure() With {.value1 = "Value2", .value2 = 2})
ScrList.Add(New IScrSubstructure() With {.value1 = "Value3", .value2 = 3})
CheckListSubstrs(ScrList, AddressOf MyIScrSub)
CheckListSubstrs(ScrList, AddressOf MyOtherIScrSub)
As an note, in the CheckListSubstrs sub I wrote:
If Substrs?.Count > 0 Then
(...)
End If
so you can handle null values for the IScrNamedObjectList parameter:
(this syntax requires VB.Net 14 or newer)
CheckListSubstrs(nothing, AddressOf MyIScrSub)
but you could also write:
If Substrs IsNot Nothing Then
(...)
End If
Public Class Population
Dim tours() As Tour ' Tour is a class and I have to make and object array
Public Sub New(ByVal populationSize As Integer, ByVal initialise As Boolean)
Dim tours As New Tour(populationSize) '
If initialise Then
' Loop and create individuals
For i As Integer = 0 To (populationSize - 1)
Dim newTour As New Tour()
newTour.generateIndividual()
saveTour(i, newTour)
Next i
End If
End Sub
Public Sub saveTour(ByVal index As Integer, ByVal tour As Tour)
tours(index) = tour ' getting error in this line
End Sub
same code in java is in this link
It's been a while that I've done VB but I think your DIM-statement in the New-method creates a new local variable tours that hides the global variable tours.
Try this:
Public Class Population
Dim tours() As Tour
Public Sub New(ByVal populationSize As Integer, ByVal initialise As Boolean)
tours = New Tour(populationSize) '
If initialise Then
' Loop and create individuals
For i As Integer = 0 To (populationSize - 1)
Dim newTour As New Tour()
newTour.generateIndividual()
saveTour(i, newTour)
Next i
End If
End Sub
Public Sub saveTour(ByVal index As Integer, ByVal tour As Tour)
tours(index) = tour
End Sub
Try,
Public Sub New(ByVal populationSize As Integer, ByVal initialise As Boolean)
ReDim tours(populationSize)
If initialise Then
' Loop and create individuals
For i As Integer = 0 To (populationSize - 1)
Dim newTour As New Tour()
newTour.generateIndividual()
saveTour(i, newTour)
Next i
End If
End Sub
I have a class with the following properties:
Dim pBonds() as string
Private Property Get Bonds() As String
Bonds = pBonds
End Property
Private Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Private Property Let Bond(index As Long, strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBond(index) = strValue
End Property
when I try:
Set o = New CBondBasket
For k = LBound(arr) To UBound(arr)
o.Bond(k) = arr(k)
Next k
I get error Method or data member not found
Any idea where that comes from?
made the changes
marked them as public now and added initialization and byval (got me another error w/o it)
Private Sub Class_Initialize()
ReDim pBonds(0)
End Sub
Public Property Get Bonds() As String()
Bonds = pBonds
End Property
Public Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Public Property Let Bond(ByVal index As Long, ByVal strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBonds(index) = strValue
End Property
error is: Definitions of property procedures for the same property are inconsistent or property procedure has an optional parameter, a ParamArray or an invalid set final parameter can anyone help me with that? thanks
You also need to initialise the pBonds array or you will get an error when calling UBound the first time:
Main module
Option Explicit
Sub testClass()
Dim o As CBondBasket
Dim k As Long
Dim arr As Variant
arr = Array(1, 2, 3, 4, 5)
Set o = New CBondBasket
For k = LBound(arr) To UBound(arr)
o.Bond(k) = arr(k)
Next k
For k = LBound(o.Bonds) To UBound(o.Bonds)
Debug.Print o.Bond(k)
Next k
End Sub
Class CBondBasket
Private pBonds() As String
Private Sub Class_Initialize()
ReDim pBonds(0)
End Sub
Public Property Get Bonds() As String()
Bonds = pBonds
End Property
Public Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Public Property Let Bond(index As Long, strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBonds(index) = strValue
End Property
Your class methods are marked Private if you want to expose them to automation clients make them Public.
(You also need parens to return an array: Public Property Get Bonds() As String())
Option Compare Database
Option Explicit
Public Function test1() As Integer
Dim sdate(2) As Date
Dim edate(2) As Date
Dim serdat As Class_erviceDate
sdate(1) = #1/2/2015#
edate(1) = #10/21/2015#
sdate(2) = #2/5/2015#
edate(2) = #12/25/2015#
Set serdat = New Class_ServiceDate
serdat.serviceStart = sdate
serdat.serviceEnd = edate
Debug.Print serdat.serviceStart(1), serdat.serviceEnd(1)
Debug.Print serdat.serviceStart(2), serdat.serviceEnd(2)
End Function
Option Compare Database
Option Explicit
Private f_datServiceStart As Variant
Private f_datServiceEnd As Variant
Public Property Get serviceStart() As Variant
serviceStart = f_datServiceStart
End Property
Public Property Let serviceStart(Value As Variant)
f_datServiceStart = Value
End Property
Public Property Get serviceEnd() As Variant
serviceEnd = f_datServiceEnd
End Property
Public Property Let serviceEnd(Value As Variant)
f_datServiceEnd = Value
End Property
The following code is excerpted from a larger procedure (the surrounding code is not relevant). Can anyone explain why I am unable to get the second ContainsKey line to return True? Hint: try this on a worksheet with just a few populated cells to reduce looping.
For Each ws As Excel.Worksheet In Wb.Worksheets
Dim dic As New Dictionary(Of Excel.Range, String)
rngUsed = ws.UsedRange
For Each cell As Excel.Range In rngUsed
dic.Add(cell, "test")
'THE FOLLOWING TWO MESSAGES SHOULD DISPLAY THE SAME RESULT, BUT DO NOT. WHY???
MsgBox(dic.ContainsKey(cell)) 'Returns True
MsgBox(dic.ContainsKey(ws.Range(cell.Address))) 'Returns False
Next
Next
UPDATE: I have added the following code and it seems to be working:
Dim dic As New Dictionary(Of Excel.Range, String)(New MyComparer()) 'replaces line from above
Class MyComparer
Implements IEqualityComparer(Of Excel.Range)
Public Function Equals1(ByVal x As Excel.Range, ByVal y As Excel.Range) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Excel.Range).Equals
If x.Address(External:=True) = y.Address(External:=True) Then
Return True
Else
Return False
End If
End Function
Public Function GetHashCode1(ByVal obj As Excel.Range) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Excel.Range).GetHashCode
Return obj.Count.GetHashCode
End Function
End Class
When an object is used as the key for the dictionary, .Net uses the GetHashCode to generate the key that is used in the underlying hashtable. Since you are using two different objects, you will get different values.
See the MSDN documentation for more details.
A better approach would be to turn the range into a string representation and use that as the key.
Dim dic As New Dictionary(Of Excel.Range, String)(New MyComparer()) 'replaces line from above
Class MyComparer
Implements IEqualityComparer(Of Excel.Range)
Public Function Equals1(ByVal x As Excel.Range, ByVal y As Excel.Range) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Excel.Range).Equals
If x.Address(External:=True) = y.Address(External:=True) Then
Return True
Else
Return False
End If
End Function
Public Function GetHashCode1(ByVal obj As Excel.Range) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Excel.Range).GetHashCode
Return obj.Count.GetHashCode
End Function
This is the solution. Note that GetHashCode used in this custom comparer is very slow, so if anyone has an idea to speed this up, I'd love to hear it. #competent_tech, I have to use objects for the keys, since there is no string representation for a range that is unique and not subject to change (like how an address changes when adding/deleting rows, for example).