My question is regarding the concept for working of ByVal in vb.net.
Here is the code:
Private Sub ManipulateDetails()
Dim tObject1 as New customdatatype
tObject1.sName = "Stack"
tObject1.sLastName = "Over"
GetManipulateDetails(tObject1)
End Sub
Private Function GetManipulateDetails(ByVal tObject1 as customdatatype)
tObject1.sName = "Stack-Over-Flow"
tObject1.sLastName = "Flow-Over-Stack"
Return tObject1
End Function
In the above code snippet I am sending tObject1 as ByVal in the GetManipulateDetails function, when the values are changed in this subroutine the object which is returned back, manipulates the actual object which was passed.
i.e. if I quickwatch the object in the method ManipulateDetails I can see the manipulated details.
Also if I am returning the object in the subroutine function the value is getting reflected in the original object which was passed.
as the value is getting changed even without returning the object from the function GetManipulateDetails, I am confused whether this is happening because of ByRef?? or there is some other mechanism which is making this work.
It may be clearer if we use different names:
Private Sub ManipulateDetails()
Dim tObject1 as New customdatatype
tObject1.sName = "Stack"
tObject1.sLastName = "Over"
GetManipulateDetails(tObject1)
End Sub
Private Function GetManipulateDetails(ByVal tother as customdatatype) as customdatatype
tother.sName = "Stack-Over-Flow"
tother.sLastName = "Flow-Over-Stack"
Return tother
End Function
Before you call GetManipulateDetails, tObject1 is a reference to an object of type customdatatype. When you call GetManipulateDetails, tother gets a copy of tObject1. Importantly, what this means is that now, tObject1 and tother are both references to the same object. What was copied was the reference, not the object. Within GetManipulateDetails, it can use its copy of the reference to access the object and make changes to it.
ByVal parameters are always copied - but parameters are either value types or references. They're never reference types (aka objects) themselves.
Related
I am stuck trying to handle an event raised by a Late Bound Object.
I got as far as Loading the assembly (sLibFile contains the location of the Library):
moAssembly = Reflection.Assembly.LoadFile(sLibFile)
Getting the type of object I'm creating is no problem
moType = moAssembly.GetType("Some.Object")
And creating the object is a doddle....
moObject = Activator.CreateInstance(moType)
But then I'm stuck. The object I have instantiated exposes an event "GetField" with the following signature:
Event GetField(FieldName As String, nPageNumber As Integer, ByRef sValue As String)
I wrote a sub to deal with this, but .... how do I add this Sub as the Handler for this event? AddHandler only works with early bound objects.
I can get the Event alright.....
oEvent = moType.GetEvent("GetField")
or perhaps
oEvent = moObject.GetType().GetEvent("GetField")
But after that I'm stuck.... How do I then link that to the Sub I wrote (I named the Sub moObject_GetField)?
oEvent.AddEventHandler(moObject, AddressOf moObject_GetField)
This raises the error "'AddressOf' expression cannot be converted to '[Delegate]' because type '[Delegate]' is declared 'MustInherit' and cannot be created."
Now what?
I'm trying to create a class with a Collection in it that will hold other CASN (kind of like a linked list), I'm not sure if my instantiation of the class is correct. But every time I try to run my code below, I get the error
Object variable or With block not set
CODE BEING RUN:
If (Numbers.count > 0) Then
Dim num As CASN
For Each num In Numbers
If (num.DuplicateOf.count > 0) Then 'ERROR HERE
Debug.Print "Added " & num.REF_PO & " to list"
ListBox1.AddItem num.REF_PO
End If
Next num
End If
CLASS - CASN:
Private pWeek As String
Private pVendorName As String
Private pVendorID As String
Private pError_NUM As String
Private pREF_PO As Variant
Private pASN_INV_NUM As Variant
Private pDOC_TYPE As String
Private pERROR_TEXT As String
Private pAddressxl As Range
Private pDuplicateOf As Collection
'''''''''''''''' Instantiation of String, Long, Range etc.
'''''''''''''''' Which I know is working fine
''''''''''''''''''''''
' DuplicateOf Property
''''''''''''''''''''''
Public Property Get DuplicateOf() As Collection
Set DuplicateOf = pDuplicateOf
End Property
Public Property Let DuplicateOf(value As Collection)
Set pDuplicateOf = value
End Property
''''' What I believe may be the cause
Basically what I've done is created two Collections of class CASN and I'm trying to compare the two and see if there are any matching values related to the variable .REF_PO and if there is a match I want to add it to the cthisWeek's collection of class CASN in the DuplicateOf collection of that class.
Hopefully this make sense... I know all my code is working great up to this point of comparing the two CASN Collection's. I've thoroughly tested everything and tried a few different approaches and can't seem to find the solution
EDIT:
I found the error to my first issue but now a new issue has appeared...
This would be a relatively simple fix to your Get method:
Public Property Get DuplicateOf() As Collection
If pDuplicateOf Is Nothing Then Set pDuplicateOf = New Collection
Set DuplicateOf = pDuplicateOf
End Property
EDIT: To address your question - "So when creating a class, do I want to initialize all values to either Nothing or Null? Should I have a Class_Terminate as well?"
The answer would be "it depends" - typically there's no need to set all your class properties to some specific value: most of the non-object ones will already have the default value for their specific variable type. You just have to be aware of the impact of having unset variables - mostly when these are object-types.
Whether you need a Class_Terminate would depend on whether your class instances need to perform any "cleanup" (eg. close any open file handles or DB connections) before they get destroyed.
I wrote a COM-Visible DLL with this piece of code (VB.NET)
' .NET Class with implementing an interface to make it visible
Public Class VisibleClass
Public Sub callableSub(ByVal names As ACustomCollection, _
Optional ByVal doSomething As Boolean = True) _
Implements IVisibleClass.visibleCallableSub
Me.doSub(names.process, doSomething)
End Sub
End Class
' Interface for COM-visibility
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface IVisibleClass
Sub visibleCallableSub(ByVal names As ACustomCollection, _
Optional ByVal doSomething As Boolean = True)
End Interface
Here is also the ASP web page creating the object and invoking its methods :
' stuff.asp
Dim visibleObject
Dim aCustomCollection
Set visibleObject = getNewVisibleInstance (aCollection)
Set aCustomCollection = createEmptyCollection
aCustomCollection.add someStuffs
aCustomCollection.add otherStuffs
aCustomCollection.add lastStuffs
' These calls work:
visibleObject.visibleCallableSub(aCustomCollection)
visibleObject.visibleCallableSub(aCustomCollection), False
visibleObject.visibleCallableSub(aCustomCollection), True
Call document.fetchPropertyCollection ((properties), false)
' These ones don't:
visibleObject.visibleCallableSub someparams
visibleObject.visibleCallableSub someparams, False
visibleObject.visibleCallableSub someparams, True
Call document.fetchPropertyCollection (properties, false)
Non working calls produce the following error :
Invalid procedure call or argument
I don't understand why I have to put parenthesis. I know this tells the interpreter to make a copy before passing theme, but not why it's mandatory.
Note : It's the same issue than this one, i.e. about passing a reference where a copy is required. However, the question was told like it's due to "passing the return value of another function", which make it harder to reach through research.
If the called Sub/Function asks for a value (ByValue), you can't pass a reference ('pointer'). Putting the 'make a copy' parentheses around the argument makes sure the called Sub/Function gets a value.
To make your intention explicit, you should write:
visibleObject.visibleCallableSub (aCustomCollection), False
Cf. same error, parentheses, adventures.
I thought this might be simple to achieve, but I can't figure out how to make it work.
I have a series of fields within a class that I want to check the value of. So instead of writing the same if ... then ... else statement I thought passing this through a private method would be ideal.
Private Sub checkParameter(ByRef p_param As Object, ByRef p_private_field As Object, p_exception As String)
If Not p_param Then
Throw New Exception(p_exception)
Else
p_private_field = p_param
End If
End Sub
Of course the problem is that I want the p_private_field to be the actual private field I am passing in.
So if I called the method with the following code:
checkParameter(i_input_folder, "p_input_folder", "Input folder must be supplied")
Then the method would check that i_input_folder did have a value and if so then assign it to the private field p_input_folder and if not throw the exception with the message provided.
Any help would be greatly appreciated.
Cheers
Seems just like a little typo
checkParameter(i_input_folder, "p_input_folder", "Input folder must be supplied")
You pass in your second parameter as string (the name of the filed i guess), but you have to pass the object itself
checkParameter(i_input_folder, p_input_folder, "Input folder must be supplied")
I have an array of objects in an excel vba project. I have created another instance of the same class and set 1 of its properties. I am then trying to search through the array of objects to find the object in the array that matches the current one on the same property. I would like to set the current object to the one in the array inside one of the current object's methods using the self reference Me.
I tried:
Set Me = objectArray(index)
This does not work. It says that this is an improper use of the Me keyword. Is there a way to set the current object to another object of the same type? Thanks!
Edit:
I have an object that has child objects:
Me.friShift.shiftType.loadFromArray
Here, shiftType is the object of type CVocabulary, which is my self defined class. It has a sub called loadFromArray that looks like this:
Public Sub loadFromArray()
Dim index As Integer
index = searchVocabArray(Me.typed)
If (index = -1) Then
Exit Sub
End If
Set Me = vocabArray(index)
End Sub
vocabArray() is a global array containing CVocabulary objects.
If it is not possible to Set an object from within itself, I can try something else. This is just the easiest and most direct way of doing this. I'm sure I can just set each parameter from the current object to the value of the parameter from the object in the array, but if it was possible to do something like the above, that would have been my preferred method.
You can do it by giving itself to the function as a parameter. I'll show it in VBScript because the classes are more clear, but the concept is the same as in VBA:
public myObject
set myObject = new x
myObject.ChangeMe MyObject
msgbox typename(myObject) ' <- outputs 'y'
class x
public sub changeMe(byref object)
set object = new y
end sub
end class
class y
' just an empty class
end class
But this is not a good programming pattern and could cause messy code (maintenance and debugging would be an issue) and even memory leaks. You should create an (Abstract) Factory, Builder or Provider that returns an object as you ask for it.
Factory: creates a new predefined object
Builder: creates a new object that is configured in the builder
Provider: returns an existing object that is predefined earlier
I don't beleive you can use Me in this context - you are trying to use Me as it was used in VB6 (which was equivalent to 'this' in C#). This is not appropriate in VBA.
Without some code snippets its hard to see what you are doing. Can you perform the search in a module and create instances of this class there? You can then do:
Set class2 = objectArrayofClass1(index)
As you've already seen that Me cannot be changed. You can handle memorized objects through
a function in a public Module like basExternal:
Public Function loadFromArrayByIndex(ByVal lIndex)
dim xobj as Object
Set xobj = vocabArray(lIndex)
'
' do modifications and handling on this object:
' ...
'
End Function
.