Dynamicaly referencing class based on a string variable - string

I'm fairly new to vba. I have a vba script that basically connects to my database using the ADODB class and returns a recordset with a bunch of information in. One column is entitled Class and it will contain the name of a Class that i'm looking to use in vb, the Class I want to use will change based on the value in the row. Is there a way I can declare and instantiate an object of this class based on the string I receive from each field under the column class. The purpose being that I need information for each row based on the class it contains without having to account for all possibilities.
for example
Dim Class as string
Class = Array(1, 4) ' Array(1, 4) contains Maths Class
Dim x as Class
Set x = New Class
if for example the particular Class is Math that would equate to
Dim x as Math
Set x = New Math
Is there a Function that could return the Class based on the string value? Thanks

Related

VBA: Can't set a variable of a Structure within a class

I have a class, implementing two interfaces, one interface for normal use, and one interface that only reveals a function to create the class with initial parameters. The class has a field that is a Structure with two variables within, I am using the Get property of the class to get the structure, and then using dot notation to access the field within the structure, and then I am trying to set it to a number, but it never works. If I try to access the structure with the private variable within the class it works, but I want to be consistent and only use the properties to modify it within the Create function.
Public Function Create(WorksheetName As String, Optional CurrentRow As Long = 4) As ISheetInfo
With New clsSheetInfo
Set .WS = ThisWorkbook.Worksheets(WorksheetName)
Set .Cols = CreateColumnDictionary(.WS, 3)
Let .Rows.Current = CurrentRow
Let .Rows.Final = .WS.Cells(.WS.Rows.Count, 1).End(xlUp).Row
Set Create = .Self
End With
End Function
When I step through, it first goes to this property (RowData is the UDT with two fields, .Current and .Final:
Property Get Rows() As RowData
Rows = pRows
End Property
But then after the assignment, .Rows.Current is still 0, I'm not sure why.

Worksheets vs. Worksheets(1), can't I do this from .net interop?

Our object model contains a class called Unit and a collection of these called Units (which is stored in a Dictionary). These objects have unique Names and Keys (they originally came from a SQL db that enforced this) so I have added:
Public Units(N as String) As Unit ...
Public Units(K as Integer) As Unit...
which return a Unit object from the Units collection.
In Excel VBA, one can refer to most objects using similar methods; Worksheets(1) returns the first sheet, while Worksheets("Bob") returns the named sheet. But they have one additional method, Worksheets, which returns the entire collection. It's as if they have this method...
Public Worksheets() As List(Of Worksheet)
But you can't use List in interop (right?) so it's more like...
Public Worksheets() As ArrayList
So how would I do the same basic API in .net with interop? That is, have three methods...
Public Units(N as String) As Unit ...
Public Units(K as Integer) As Unit...
Public Units() As ArrayList...
As I understand it only the first method of a given name is exported (is this correct?). So how does Excel do it, and can I fake that in .net?
VBA's Worksheets is not a method. It is a class, Worksheets, that has a default property Item that accepts a parameter of type Variant. There is no overloading (COM does not support it), it's just that Variant can hold both a number or a string.
If you want a similar structure in VB.NET, you can have a collection class that implements a default property as VB.NET understands it, and this time you can overload it.
Public Class UnitsCollection
Default Public ReadOnly Property Item(ByVal i As Integer) As Unit
Get
Return ...
End Get
End Property
Default Public ReadOnly Property Item(ByVal i As String) As Unit
Get
Return ...
End Get
End Property
End Class

Use ObjPtr(Me) to return the Name of a Custom Class Instance?

I understand that ObjPtr will return the address of an object in memory and that it points to a structure called IUNKNOWN and that there is some kind of Interface definition encoded in that to expose the Object structure, but I couldn't figure out how to determine the interfaces for a VBA Custom Class Object and how to use that to return the Name property of an Object.
It's more "nice to have" than essential, but I just want to know the name of an object instance at run time so that I can include it in my trace messages.
Can anyone explain how to do this or, better yet direct me to a reference so I can figure it out?
EDIT
To re-state my aim:
To make a custom class objects that is able to figure out the name of its particular instance.
For example
Dim oObject1 as Class1, oObject2 as Class1
Set oObject1 = New Class1
Set oObject2 = New Class1
Debug.Print oObject1.instanceName & " " & oObject2.instanceName
In the immediate window:
oObject1 oObject2
Is this possible in VBA?
If VBA runtime has a Symbol Table - since it is interpretive I think maybe it does - and I had a way of exposing it, then I could make a Property Get procedure to access the symbol Table and search on the Address - ObjPtr(Me) - to return the semantic name of the instance of the class.
I'm pretty sure this is a dumb question but, hopefully, the process of realising its a dumb question is helpful to my understanding.
Example of a Symbol Table
Address Type Name
00000020 a T_BIT
00000040 a F_BIT
00000080 a I_BIT
20000004 t irqvec
20000008 t fiqvec
2000000c t InitReset
20000018 T _main
20000024 t End
Take NO for an answer. It's not possible to return an instance name as a String literal in VBA.
I still don't understand the reason you may want to do that... Anyway
The easiest way to know each instance code name would be to create a property for a class that stores the actual name. This would only expose the name as a String property and not an actual reference to the object - it already has a reference - itself!
So create a class module
Class1
Option Explicit
Public MyName as String
and in Module1 all it takes is
Option Explicit
Sub Main()
Dim c As Class1
Set c = New Class1
c.MyName = "c"
Debug.Print c.MyName
End Sub
And there you go :)
Another way would be to create a Dictionary to store both KEY/VALUE pairs.
Sub Main()
Dim c As Class1
Set c = New Class1
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "c", c
Debug.Print dict.Exists("c")
End Sub
Now, it's possible to actually do what you want but it would be a really ugly way. Here's how as I am not going to demonstrate.
You would create an instance of a custom class. Using ObjPtr you can get it's reference in memory. Then you would need a mechanism that scans your module code line by line and finds names of all variables you've dimensioned. Once you retrieve a list of all variables you would need a mechanism which tries to create an instance of the same type (class). Once you get past that point you could try to myNewObj = c ("c" would be the obj instance) programmatically. If that succeed then you would do ObjPt for both and match their addresses in memory - you get a match you know the variable name. Grree please do not do it that way :P
TypeName(obj) will return the Type of any variable in VBA:
Dim c as Class1
set c = new Class1
Debug.print TypeName(c) '==> "Class1"
FYI, I've also historically wanted to access the symbol table also. The idea was to get local variables from the previous scope by name. In that way you could make string interpolation:
a = "World"
Debug.Print StringInterp("Hello ${a}")
https://github.com/sancarn/VBA-STD-Library/blob/master/docs/VBAMemoryAnalysis.txt
https://github.com/sancarn/VBA-STD-Library/blob/master/docs/VBAMemoryAnalysis2.txt
No luck making a general function yet.

excel 2003 vba.. writing arraylist

I am trying to create ArrayList of Class like in Java, in Visual Basic Excel 2003.
Java
List<Employee> employees = new ArrayList<Employee>();
Employee employee = new Employee();
employee.setName("tom");
employees.add(employee);
VB
Dim resultList As New Collection
Dim Manager As Employee
Manager.Name = "df"
resultList.Add ("rr") 'correct
resultList.Add (Manager) 'error
But this gives the following error:
only user-define types defined in public object modules can be coerced
to or from a variant or passed to late-bound functions
There is no type information associated with a UDT so it can't be added to a collection as there is no way to reliably convert to/from a variant as the number & types of its members is unknown.
You can either replace the Employee Type with a Class or as you don't appear to be using a key, a typed array: arr() as Employee

Visual basic string issues

Every time i try to attribute any type of string to this i get Object reference not set to an instance of an object. I have tried every combination of possible way to handle the string, convert it to a string again and all the fuzz. It's very frustrating and i guess it's some kind of base principle of the structure/class usage and the string array or whatnot (which is also very dumb)
Private Class movie
Public name As String
Public actors As String
Public year As Integer
Public country As String
Public votes As Integer
End Class
Private movies() As movie
If File.Exists(OpenFileDialog1.FileName) Then
lblPath.Text = OpenFileDialog1.FileName
Dim iFile As New StreamReader(lblPath.Text)
While Not iFile.EndOfStream
current = iFile.ReadLine
movies(i).name = "sasasasa"
lbMovies.Items.Add(movies(i).name)
i = i + 1
End While
End If
these are the code parts where i use it
You are creating an empty array of movie objects, as was pointed out previously. Consequently movies(i) is Nothing. When you try to access a member (movies(i).name) the appropriate exception is generated. Note that your code does not even reach the assignment operator = but fails prior to that. In other words, this has nothing to do with strings altogether; you will get the same error if you write movies(i).votes = 42 instead. To fix your code you will first have to create a movie object, populate it and append it to your array.

Resources