How can I access a class which is a member of another class? - vb6

Please note that this is VB6!
I have a class named Kostenvoranschlag.
It has a member array of a class named Positionen.
I have declared it as such
Class Kostenvoranschlag
Private m_Positionen() As position
Public Function Positionen() As position
Positionen = m_Positionen
End Function
End Class
Class Position
Public Betrag As Currency
End Class
The following does not compile and throws "Wrong number of arguments or invalid property".
Dim k As New kv
kv.Positionen(0).Betrag = 5
How can I access this class from outside?
ps: I have also tried these 2 versions, but they wouldn't compile either:
Friend Function Positionen() As position
Positionen = m_Positionen
End Function
Public Property Get Positionen() As position()
Positionen = m_Positionen
End Property

As noted in the question, this syntax does not work for whatever reason:
k.Positionen(0).Betrag = 5
But this syntax DOES work:
Dim p() As Position
p = k.Positionen
p(0).Betrag = 5
However, I would typically set this up using collections instead of arrays. If you do this, then the original syntax works:
k.Positionen(1).Betrag = 5

Related

Using special characters in Enum value

I'm refactoring an ASP.NET MVC application that contains a Grid that uses remote filtering, sorting and pagination, it currently uses a string to pass the comparison operator that should be applied, I'd like to change that into an Enum:
Public Class MyController
Inherits Controller
Public Function GetOrders(filterModels As List(Of FilterModel)) As JsonResult
'A member of FilterModel is of type EnumComparisonOperators here
...
End Function
End Class
Public Enum EnumComparisonOperators
<Description("=")>
Equals = 0
<Description("<>")>
NotEquals = 1
<Description("<=")>
LessThanOrEquals = 2
<Description(">")>
GreaterThan = 3
<Description(">=")>
GreaterThanOrEquals = 4
End Enum
In the View:
//In the real code, my ajax call is in a callback from a third party
//component that just passes these loadOptions
var loadOptions = {
filterModel: {
operator: "=" //Replacing this string with "Equals" causes the code to work
//But my application logic needs a "=" sign, so I'd like to avoid
//converting back and forth
}
};
//The exception gets thrown the server when it receives this post call
$.post("/My/GetOrders", loadOptions);
My problem is that this results in an exception (= is not a valid value for EnumComparisonOperators.) as the calling grid component uses the string "=" for the "equals" operation and the controller doesn't parse that automatically, so my question is:
Is there a way for me to change/decorate/configure the Enum, so that "=" is recognized by the controller as a valid value as opposed to "Equals".
So in essence I'm trying to achieve the behavior I would get if = were the name of my enum's value, but = is a special character so I used Equals and am looking for configuration that would make it behave like =, that means, parsing and serialization should use =
The exception "= is not a valid value for EnumComparisonOperators" indicates that you're passing string which doesn't recognized as proper enum value (which contains integer indexes). You can keep <Description> attributes for each enum members (because you can't use operator symbols as enum member like EnumComparisonOperators.= or EnumComparisonOperators.<=), but it's necessary to write your own function to set enum member value from operator key in JSON using reflection like example below (adapted from this reference):
Public Function GetDescription(Of T)(ByVal value As T) As String
Dim field As FieldInfo = value.[GetType]().GetField(value.ToString())
Dim attributes As DescriptionAttribute() = CType(field.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If attributes IsNot Nothing AndAlso attributes.Length > 0 Then
Return attributes(0).Description
Else
Return value.ToString()
End If
End Function
Public Function GetEnumValueFromOperator(Of T)(ByVal op As String) As T
Dim array As Array = [Enum].GetValues(GetType(T))
Dim list = New List(Of T)(array.Length)
For i As Integer = 0 To array.Length - 1
list.Add(CType(array.GetValue(i), T))
Next
Dim dic = list.[Select](Function(x) New With {
.Value = v,
.Description = GetDescription(x)
}).ToDictionary(Function(s) s.Description, Function(s) s.Value)
Return dic(op)
End Function
Afterwards, call the function above inside controller action (depending on your current implementation, these codes are subject to change):
Model
Public Class FilterModel
Public Property operator As String
' other properties
End Class
Controller
<HttpPost()>
Public Function GetOrders(filterModels As List(Of FilterModel)) As JsonResult
' check against null or zero length (filterModels.Count = 0) first
For Each fm As FilterModel In filterModels
Dim selectedOperator = GetEnumValueFromOperator(Of EnumComparisonOperators)(fm.operator)
Select Case selectedOperator
Case 0 ' Equals
' do something
End Select
Next
' other stuff
Return Json(...)
End Function
See also this fiddle for another usage example.
Note: Another available alternative is using EnumMemberAttribute like <EnumMember(Value := "=")> for every enum members and create a function to read that value as described in this issue.

HP-UFT / VBscript - Setting an array value through a function with an array index reference

I have an issue with assigning/retrieving correct values to/from an array which is within a class in VBScript. Whenever I try to set the array's value through a stand-alone function, it just does not work and I keep getting the old array values.
Here is an example code:
Class NewClass
Public TestValues
Public Sub Init()
TestValues = array("value0", "value1", "value2")
End Sub
End Class
Class NewFunctions
Public Function GetValue(xRef)
GetValue = xRef(2)
print "Value within Function: " & xRef(2)
End Function
Public Sub SetValue(xRef, xValue)
xRef(2) = xValue
print "Value within Sub: " & xRef(2)
End Sub
End Class
Dim MyClass, MyFunction
Set MyClass = New NewClass
Set MyFunction = New NewFunctions
Now, when I try to set the index 2 of the given array MyClass.TestValues with the SetValue Sub, it claims the new value has been set, but when I then call GetValue for this array or print out the content of the MyClass.TestValues(2) directly, I am still getting the old values:
MyFunction.SetValue MyClass.TestValues, "newvalue2"
It returns: Value within Sub: newvalue2
But when I retrieve the value with GetValue:
MyFunction.GetValue MyClass.TestValues
It returns: Value within Function: value2, which is the old value.
When I, however, set the array directly:
Myclass.TestValues(2) = "newvalue2"
then calling with:
MyFunction.GetValue MyClass.TestValues
gives me correct result: Value within Function: newvalue2
I am not sure whether this is a general VBScript behavior and I am making a mistake in hoping to change array values in this 'dirty' manner or whether this is strictly HP-UFT (Unified Functional Testing) related, since this is where I could observe this.
I am not a profficient VBScripter either so I appreciate any help.
This is a documented behaviour
Argument in a Class
If the parameter is specified as ByRef, the argument is passed by
value if the variable sent as an argument is in a class

How to expose an array of UDTs through a property?

I have a class cDept which has a UDT defined.
public type udtEmp
Name as string
Id as long
end type
I have an array defined:
private m_Emps() as udtEmp
I want to expose the array through a property. I tried the following:
Public Property Get Employees() As udtEmp()
Employees= m_Emps
End Property
So far everything compiles. Now I instantiate the class and try to access the property.
dim myUdt as udtEmp
dim oDept as cDept
set oDept = new cDept
myUdt = oDept.Employees(1) ' -- error
I get an error stating Wrong number of arguments or invalid property assignment.
What am I missing?
(Not tested)
I think your property access is trying to use the '1' as an argument to the property (which has no arguments), thus the 'wrong number' error. Rather than trying to property get the array and then index access the array, will it work to have the property get (or a different one) return the desired array element?
Public Property Get Employees(ndx as long) As udtEmp
Employees= m_Emps(ndx)
End Property

Why would one declare both a Let and Set property accessor in VB6

In an older project I found a property declaration in a class module which looks like the following...
Public Property Get DrawObject() As Object
Set DrawObject = m_obj
End Property
Public Property Let DrawObject(obj As Object)
Set m_obj = obj
Draw
End Property
Public Property Set DrawObject(obj As Object)
Set m_obj = obj
Draw
End Property
I would like to know why the DrawObject property has both a Let and Set accessor defined; what could be the purpose of such a declaration?
The only reason would be to allow/support both assignment syntaxes:
set instance.DrawObject = obj
and
instance.DrawObject = obj

Attempting to learn polymorphism, etc. in VB6, but my code doesn't do what I want it to

Here's what I've got on a command button; it's just creating variables and attempting to output their ID (which should be an instance variable inherited from the base class.)
Private Sub Command1_Click()
Dim ball1 As Ball, ball2 As Ball
Dim cube1 As Cube, cube2 As Cube
Set ball1 = New Ball
Set cube1 = New Cube
Set cube2 = New Cube
Set ball2 = New Ball
MsgBoxTheID (ball1) 'errors; should be 0
MsgBoxTheID (ball2) 'errors; should be 3
MsgBoxTheID (cube1) 'errors; should be 1
MsgBoxTheID (cube2) 'errors; should be 2
Call ball1.MsgBoxID ' works; displays 0
Call ball2.MsgBoxID ' works; displays 3
Call cube1.MsgBoxID ' works; displays 1
Call cube2.MsgBoxID ' works; displays 2
End Sub
Modeul1.bas:
Global globalID As Integer
Public Sub MsgBoxTheID(theObj As BaseObj)
' this function is meant to accept objects of type Ball, Cube, and BaseObj
MsgBox theObj.ID
End Sub
BaseObj Class Module:
Public ID As Integer
Public isVisible As Boolean
Public Sub setVisiblity(newVis As Boolean)
isVisible = newVis
End Sub
Public Sub MsgBoxID()
MsgBox ID
End Sub
Private Sub Class_Initialize()
ID = globalID
globalID = globalID + 1
End Sub
Ball Class Module:
Implements BaseObj
Private theObj As BaseObj
Public radius As Double
Private Property Let BaseObj_ID(ByVal RHS As Integer)
End Property
Private Property Get BaseObj_ID() As Integer
End Property
Private Property Let BaseObj_isVisible(ByVal RHS As Boolean)
End Property
Private Property Get BaseObj_isVisible() As Boolean
End Property
Public Sub MsgBoxID()
Call theObj.MsgBoxID
End Sub
Private Sub BaseObj_MsgBoxID()
Call theObj.MsgBoxID
End Sub
Public Sub BaseObj_setVisiblity(newVis As Boolean)
End Sub
Private Sub Class_Initialize()
Set theObj = New BaseObj
End Sub
Cube Class Module:
Implements BaseObj
Private theObj As BaseObj
Public sideLength As Double
Private Property Let BaseObj_ID(ByVal RHS As Integer)
End Property
Private Property Get BaseObj_ID() As Integer
End Property
Private Property Let BaseObj_isVisible(ByVal RHS As Boolean)
End Property
Private Property Get BaseObj_isVisible() As Boolean
End Property
Public Sub MsgBoxID()
Call theObj.MsgBoxID
End Sub
Private Sub BaseObj_MsgBoxID()
Call theObj.MsgBoxID
End Sub
Public Sub BaseObj_setVisiblity(newVis As Boolean)
End Sub
Private Sub Class_Initialize()
Set theObj = New BaseObj
End Sub
There are several things I don't like about this, two of which I am of the impression are unavoidable: (1) the fact that it's a mess compared to C++, and (2) the fact that the Ball and Cube classes merely contain an object of BaseObj type. They are not actually inheriting anything from BaseObj; they are only being forced to implement the same interface (whoopty doo.)
To make matters worse, and this is the one that I am truly hoping is rectifiable, they do not seem to be able to fill in for an object of the base class when it comes to parameter passing.
Am I doing something wrong?
Visual Basic 6 is not the ideal language with which to learn the "purer" form of OOP. VB6 was designed to implement a very much hybridized version of object-based programming that orbited the Microsoft Component Object Model (COM) world, with its interface inheritance orientation. VB6 does not support implementation inheritance, which tends to make the kind of polymorphism you're looking for hard to do.
There are a few tricks I recall from the VB6 era to "get around" (sort of) the implementation inheritance problem, particularly when it comes to substituting an object of a base class for a subclass. One trick I remember is to declare a property procedure of the type of the base interface that returns a reference to "Me" as the return type. That "tricks" the runtime into providing the conversion into the desired interface. There's another magic trick to make a property the "defaut" property by setting its "procedure number" to -4 in one of VB6's design dialogs.
The point? If you're really wanting to get into conventional OO programming, don't try to learn it with VB6 if you don't have to. Move up to (at least) VB.NET, C#, or Java. I don't say that as a VB6 hater - heck, knowing these stupid details paid the bills for a long time - but its a tough nut to crack to translate its own little idiosyncrasies into a good, fundamental understanding of OOP.
Good luck!
You've figured out how to fix the error, but I'll contribute the "why".
In VB6 (and VB5, etc), there are two syntaxes for invoking a method/function/subroutine/statement. The first is this:
MySubName arg1, arg2, arg3, arg4
Blech, I know it is my bias from C and Java, but I like to see parenthesis around my argument list. That sytax is this:
Call MySubName(arg1, arg2, arg3, arg4)
So those two are equivilent.
What you ran into is not the effect of the Call. What you ran into is the effect of the unneeded parenthesis in the non-Call version. The parenthesis force the statement/arg inside them to be evaluated before the rest of the statement (think math order of operation).
So this:
SomeSub (arg1)
Is like this:
temp = (arg1)
SomeSub temp
Further, objects in VB6 can have a "default property". This lets you write code like this:
Dim name as String
name = txtName
Instead of assigne the textbox object reference to name, the default property of .Text is used, and the result is like this:
Dim name as String
name = txtName.Text
So when you tried to evaluate SomeSub (arg1) I think it would attempt to locate and execute the default property of your object and pass that value to SomeSub.
Well, I figured it out, sort of.
MsgBoxTheID (ball1) 'errors; should be 0
MsgBoxTheID (ball2) 'errors; should be 3
MsgBoxTheID (cube1) 'errors; should be 1
MsgBoxTheID (cube2) 'errors; should be 2
...needs to be changed to...
Call MsgBoxTheID (ball1) 'errors; should be 0
Call MsgBoxTheID (ball2) 'errors; should be 3
Call MsgBoxTheID (cube1) 'errors; should be 1
Call MsgBoxTheID (cube2) 'errors; should be 2
... even though MsgBoxTheID has no return type, which is weird because I always thought Call was simply something that you could use to discard the return value without having to declare a variable, like so:
dim unneededVar as Integer
unneededVar = FunctionNameThatReturnsAnInteger()
But I guess not. So... I'll have to go read up on exactly what the Call statement is doing to make this example program work, but it's definitely working now. (I also had to add BaseObj_ID = theObj.ID to the Property Get BaseObj_ID() As Integer methods in the classes that were implementing BaseObj.)

Resources