Given that we have a script
Option Explicit
Class CClass
Private m_date
Private Sub Class_Initialize()
m_date = CDate("1970-01-01 00:00:00")
End Sub
Public Function Foo()
Dim d : d = Date()
WScript.Echo "d is " & FormatDateTime(d, vbGeneralDate)
End Function
Public Property Get Date()
Date = m_date
End Property
Public Property Let Date(p_date)
m_date = CDate(p_date)
End Property
End Class
Dim obj : Set obj = NEW CClass
Call obj.Foo()
How can class function CClass.Foo() call built-in VBScript function Date() without the property CClass.Date interfering?
My current solution is to introduce a dummy Date_() function which can be called instead. But that just seems wrong. I'm thinking there should be some way to specify that we want to call something outside the class scope.
I am almost positive that there is no way to do what you're asking in VBScript.
But even if you could figure out a way to do this, you really shouldn't. You need to choose names for your own functions that don't conflict with the names of built-in functions. Anything else is completely unmaintainable for a dynamic scripting language like VBScript.
Pick a different name for your Date property. Preferably something more descriptive: what kind of date does that property return? What does the date refer to? How is it likely to be used? Whatever you do, don't rename it to Date_—that's not any better.
You can call it from inside the class like:
Dim d : d = me.Date()
Me in VBScript is the same as you use This in Javascript for example
Too late to the party, but there is a solution using eval function.
see eval function docs for details
Class CClass
Private m_date
Private Sub Class_Initialize()
m_date = CDate("1970-01-01 00:00:00")
End Sub
Public Function Foo()
Dim d : d = eval("Date()")
WScript.Echo "d is " & FormatDateTime(d, vbGeneralDate)
End Function
Public Property Get Date()
Date = m_date
End Property
Public Property Let Date(p_date)
m_date = CDate(p_date)
End Property
End Class
Dim obj : Set obj = NEW CClass
Call obj.Foo()
Related
I have the following function that works fine:
Function GetAgentEmailWorksheet(AgentObjectId As String)
Dim specific_agent As clsAgent
Set specific_agent = New clsAgent
specific_agent.AgentSheetName = "agentsFullOutput.csv"
Dim id_array() As Variant
id_array = specific_agent.AgentIDArray
Dim email_array() As Variant
email_array = specific_agent.AgentEmailArray
GetAgentEmailWorksheet = vlook_using_array(AgentObjectId, id_array, email_array)
End Function
When, however, I change the last line to:
GetAgentEmailWorksheet = vlook_using_array(AgentObjectId, specific_agent.AgentIDArray, specific_agent.AgentEmailArray)
I get the following error:
Compile error:
Type mismatch:array or user-defined type expected
and it hi-lights AgentIDArray (or AgentEmailArray if I sub out the first parameter.
Why?
EDIT
here's the fuction vlook_using_array:
Function vlook_using_array(target_string As String, _
input_array() As Variant, _
output_array() As Variant)
Dim rows_dim As Long
Dim cols_dim As Integer
For rows_dim = 1 To UBound(input_array, 1)
For cols_dim = 1 To UBound(input_array, 2)
If input_array(rows_dim, cols_dim) = target_string Then
vlook_using_array = output_array(rows_dim, cols_dim)
End If
Next cols_dim
Next rows_dim
End Function
Here are the clsAgent properties:
Public Property Get AgentClientsArray() As Variant
AgentClientsArray = get_column_array(AgentClientsCol)
End Property
Public Property Get AgentIDArray() As Variant
AgentIDArray = get_column_array(1)
End Property
Public Property Get AgentEmailArray() As Variant
AgentEmailArray = get_column_array(AgentEmailCol)
End Property
Here's the function that's in the class module:
Private Function get_column_array(col_num As Integer) As Variant
' create a range out of the used range (of the sheet) in the column specified
' used to create array properties in the class
Dim total_rows As Long
total_rows = Worksheets(Me.AgentSheetName).UsedRange.rows.Count
Dim target_range As Range
With Worksheets(Me.AgentSheetName)
Set target_range = .Range(.Cells(1, col_num), .Cells(total_rows, col_num))
End With
Dim target_arr() As Variant
target_arr = target_range
get_column_array = target_arr
End Function
There's a difference between this:
Public Property Get AgentIDArray() As Variant
AgentIDArray = get_column_array(1)
End Property
and
Public Property Get AgentIDArray() As Variant()
AgentIDArray = get_column_array(1)
End Property
The first (which you have in your class) returns a Variant which happens to be an array, the second an array of variants.
So, the return value of your AgentIDArray is not consistent with the parameter types expected by vlook_using_array
I am writing my first classes.
One is cCRElist which is essentially a collection of cCRE instances (some specialized events).
I want there to be a sub or function inside cCRElist that will load all the CRE's from the worksheet into one big collection I can work with. I created the function and it worked OK when I called it from a normal code module, but then I tried to move the code into the class. Now I am having trouble calling the function LoadFromWorksheet(myWS as Worksheet).
The error is "object does not support this property or method". I have tried making it a sub, a function, making it public, not public, I have tried turning into a Property Let instead of a sub. Obviously I have a flimsy grasp on what that does. I have tried
Call CREList.LoadFromWorksheet(myWS)
and
CREList.LoadfromWorksheet myWS
Same error every time.
Here is the test code that uses the class and calls the function:
Sub TestClassObj()
Dim CRElist As cCRElist
Set CRElist = New cCRElist
Dim myWS As Worksheet
Set myWS = ThisWorkbook.ActiveSheet
CRElist.LoadFromWorksheet (myWS)
End Sub
Here is a snippet of the class cCRElist:
' **** CLASS cCRElist
Option Explicit
' This is a collection of CRE objects
Private pCRElist As Collection
Private Sub Class_Initialize()
Set pCRElist = New Collection
End Sub
Public Property Get CREs() As Collection
Set CREs = pCRElist
End Property
Public Property Set Add_CRE(aCRE As cCRE)
pCRElist.Add aCRE
End Property
Function LoadFromWorksheet(myWS As Worksheet)
Dim CRE As cCRE
Dim iFirst As Long
Dim iLast As Long
Dim i As Long
Set CRE = New cCRE
iFirst = gHeader_Row + 1
iLast = FindNewRow(myWS) - 1
' update data in CRE then add
For i = iFirst To iLast
If myWS.Cells(i, gCRE_Col) <> "" Then ' This is a CRE row
Set CRE = New cCRE
With CRE
.CRE_ID = myWS.Cells(i, gCRE_Col)
If Not IsDate(myWS.Cells(i, gCRE_ETA_Col)) Then
.ETA = "1/1/1900"
Else
.ETA = Trim(myWS.Cells(i, gCRE_ETA_Col))
End If
<... snipped ...>
End With
pCRElist.Add_CRE CRE
End If
Next
End Sub
' **** END OF CLASS cCRElist
Thanks for your expertise.
Here is what worked based on help I got in the comments. First, I did the "break in class module". In the test code, I changed the function call from:
CRElist.LoadFromWorksheet(myWS)
to
CRElist.LoadFromWorksheet myWS
Inside the class, i had to change
Set pCRElist.Add_CRE CRE
to
pCRElist.Add CRE
Then I was able to get rid of extraneous CLASS functions Add_CRE and Count.
Thanks for everyone's input. I couldn't figure out how to mark comments as accepted answers so I did this. Let me know if I need to do something differently.
Now it works!
I have been trying to implement a very simple tree structure in VBA for some basic text parsing needs. The relevant code follows.
Private pLeaves() As CTree
Private numLeaves As Integer
Private leavesLen As Integer
Private iterate As Integer
Private pParent As CTree
Public pValue As Object
Public Sub class_initialize()
ReDim pLeaves(10)
leavesLen = 10
numLeaves = 0
Set pParent = Nothing
iterate = 0
Set pValue = Nothing
End Sub
Public Sub Class_terminate()
'We'll leave it empty for now
'if it looks like it's not working right we might change it
End Sub
Public Property Get Parent() As CTree
Parent = pParent
End Property
Public Property Get Leaves() As CTree
Leaves = pLeaves
End Property
Private Property Set Parent(ByRef p As CTree)
Set pParent = p
End Property
Private Property Set value(ByRef value As Object)
Set pValue = value
End Property
Public Sub Add_Leaf(ByRef value As Object)
Dim n As Integer
n = numLeaves
If numLeaves >= leavesLen Then
ReDim Preserve pLeaves(leavesLen + 10)
leavesLen = leavesLen + 10
End If
Set pLeaves(n) = New CTree
Set pLeaves(n).Parent = Me
Set pLeaves(n).value = value
End Sub
Public Function DFFind_Leaf(value As Object) As CTree
Dim i As Integer
If pValue = value Then
Set DFFind_Leaf = Me
Return
End If
If numLeaves = 0 Then
Set DFFind_Leaf = Nothing
Return
End If
For i = 0 To numLeaves
Set DFFind_Leaf = pLeaves(i).DFFind_Leaf(value)
If DFFind_Leaf <> Nothing Then
Return
End If
Next i
Set DFFind_Leaf = Nothing
Return
End Function
When I try to call the Add_Leaf function with an object though I end up getting a
method or data member not found error from VBA on the line where I'm trying to
set pLeaves(n).Parent= me
Any idea what the reason for that might be?
Edit: ok I figured out how to fix this. Changing the set property Parent(...) from private
to public fixed the problem. So apparently I don't quite understand what private exactly does in vba. If someone wants to explain how to do this without essentially exposing the variables for anyone to set as they want I would be grateful.
Private tells the VBA interpreter that the scope of the variable is restricted to the class/module only. You can therefore access it only inside of the original class, but not from the outside. Publicon the other hand allows you to access the variable from the outside.
However, note that when working with classes, it is strongly discouraged to publicly expose any variables. Instead it is best practice to expose so called properties, using the SET PROPERTYand GET PROPERTY keywords.
I.e. instead of
Public Amount as Double
It is often better to use this approach:
Private mAmount as Double
Property Set Amount(amt as Double) 'Note that by default, properties are public
If amt > 0 Then
mAmount = amt
Else
mAmount = 0
End If
End Property
Property Get Amount as Double
Amount = mAmount
End Property
As you can see from this example, the advantage is that you can actually add additional verifications/modifications and therefore ensure the consistency.
In your example, you could consider to simply provide a method, i.e. a public sub, that adds a leave and takes care of all the settings, e.g.
Public Sub AddLeave(strName as String)
Dim objLeave as New CTree
objLeave.Init strName, Me
mLeaves.Add objLeave, strName
End Sub
Public Sub Init(strName as String, objParent as CTree)
Me.Name = strName
Set Me.Parent = objParent
End Sub
I have created a class called Class1,
and in another module, I want to use the class like this:
Dim budgeunit As Class1
Sub Creattree()
Dim lvl1p, lvl1m, lvl1dm As Class1
Set lvl1p = New Class1
lvl1p.setName ("pear")
Set lvl1m = New Class1
lvl1m.setName ("Mango")
Set budgeunit = New Class1
budgeunit.addtochildren (lvl1p), budgeunit.addtochildren(lvl1m)
End Sub
But when I compile it, it highlight the budgeunit.addtochildren(lvl1m)
and said Byref argument mismatch.
I have declare all arguments, I do not know why this happens.
The class code:
Dim Children() As Class1
Dim Parent() As Class1
Public level As Integer
Public name As String
Function setName(nm As String)
name = nm
End Function
Function addtochildren(node As Class1)
num = Children.Count
ReDim Children(num + 1) As String
Children(num) = node
node.addParent (Me)
End Function
Stefan's answer is correct, but there are other problems as well.
What's this supposed to mean?
budgeunit.addtochildren (lvl1p), budgeunit.addtochildren(lvl1m)
You can't separate two statements with a comma like that. Do you mean this?
budgeunit.addtochildren lvl1p
budgeunit.addtochildren lvl1m
Also you should revise your use of Functions, and probably replace them with Subs. You're not expecting any output from them, so...
Another thing is your use of parentheses. Arguments to subs (and functions with no return value expected) should not be surrounded by parentheses; in some cases having parentheses will make things go wrong. (With some qualification if you use the Call notation, which you do not.)
Your declaration of the variables does probably something different than you expect.
Try:
Sub Creattree()
Dim lvl1p as Class1, lvl1m as Class1, lvl1dm As Class1
In your declaration, you create two variables of type Variant, and of type Class1.
I have created two classes as interface / implementation classes, and wish to pass a particular example of one class to a method in the other. The definitions are as follows...
Class BigInt...
Option Explicit
Public Sub dothing(ByRef passed_object As MyInt)
End Sub
and an implementation BigImplementation...
Option Explicit
Implements BigInt
Public Sub BigInt_dothing(ByRef passed_obj As MyInt)
Dim i As Integer
i = passed_obj.getprop
End Sub
The class I am planning to pass is...
Option Explicit
Public Property Get getprop() As Integer
End Property
Public Property Let letprop(ByVal myval As Integer)
End Property
implemented as MyImplementation thus...
Option Explicit
Implements MyInt
Private myval As Integer
Public Property Get myint_getprop() As Integer
myint_getprop = myval
End Property
Public Property Let myint_letprop(ByVal passed_int As Integer)
myval = passed_int
End Property
I am then driving this with the following snippet of code:-
Private Sub Command_Click()
Dim myobj As MyInt
Set myobj = New MyImplementation
Dim mybigobj As BigInt
Set mybigobj = New BigImplementation
myobj.letprop = 1
Call mysub(myobj)
mybigobj.dothing (myobj) ' Line with problem
End Sub
Private Sub mysub(ByVal passed_obj As MyInt)
Dim i As Integer
i = passed_obj.getprop
End Sub
When the execution reaches the line marked, I get run-time error 438 - Object doesn't support property or method. The call to the ordinary function mysub works perfectly. Does anyone know what I am doing wrong and what I need to do to fix this?
Use either
mybigobj.dothing myobj
or
Call mybigobj.dothing(myobj)
Putting extra parentheses around a reference evaluates its default property and passes it's value as actual argument.
mybigobj.dothing requires its parameter to be a MyInt
Public Sub BigInt_dothing(ByRef passed_obj As MyInt)
Dim i As Integer
i = passed_obj.getprop
End Sub
You're passing a MyImplementation
Set myobj = New MyImplementation
So possiblly something like (my vb is rusty)
mybigobj.dothing (myobj.myint_getprop())