Dynamically typecast an object

Is there any way to dynamically (possibly using introspection) typecast an object?

Basically, I have a superclass (AutoDBClass) that has many subclasses. These are all listed out in an array in AutoDBClass.DatabaseItemTypes. This is the one place I will have to add any code if a new AutoDBClass subclass needs to be added. In many parts of the app this array gets looped through to perform functions on all of the possible subclasses, saving the time of having to write out extra code every time a new subclass is added.

Anyway, I’m trying to access these items in the following way (as an example):

[code] dim hh(-1) as autodbClass=AutoDBClass.DatabaseItemTypes

for i as integer=0 to UBound(hh)

dim rs as RecordSet = db.SQLSelect("SELECT * FROM "+hh(i).MyClassName+" WHERE 1")

if rs<>nil then
  while not rs.EOF
    hh(i) = new AutoDBClass(rs) //constructor loads properties from fields in recordset 
    someDictionary.Value(hh(i).id) = hh(i)
    rs.MoveNext
  wend
  rs.Close
end if

next[/code]

This code doesn’t quite work because all of the items get unsurprisingly added to the someDictionary as an AutoDBClass. This causes issues in various other parts of the app. Is there any way (without having to write out code for every single type of subclass) to get this working?

I’m not following the code. You get hh (the array of AutoDBClass subclasses right) and for each you do a SQLSelect but then in the while loop the hh element is replaced with a new instance. I guess hh is a copy.

So is it that you want “hh(i) = new AutoDBClass(rs)” to instantiate a new object of the same type as hh? You can do that using Introspection to get the list of constructors and then call the appropriate one. Finding that appropriate one can be tricky. Loop over the ConstructorInfos to find the one with matching parameters, but if the Super has a Constructor with the same parameters you’ll find 2 possible Constructors in the list. I forget how to differentiate them but there is a way.

Or is it something else you’re trying to do?

Perhaps you should use a class interface? The interface specifies the methods that subclasses implement, allowing you to call them without typecasting the object (or even knowing what subclass it is). Subclasses could even override the interface methods of its superclass.

I typically use introspection to load recordsets into strongly typed classes.

Dim rs as recordset = db.sqlSelect While not rs.eof dim myNewObject as someDBObjectSubClass myNewObject.Load(rs) myNewObjectList.append(myNewObject) rs.movenext Wend

The load method on the DBobject class then uses introspection to assign the properties of the class from a recordset
Is this what you’re trying to do?

Here’s what I do in one of my projects for the load method:

This loads a class via a dictionary:

Sub load(d as Dictionary) Dim myProperties() as Introspection.PropertyInfo = Introspection.GetType(me).getproperties For each prop as Introspection.PropertyInfo in myProperties if not prop.CanWrite then Continue dim propName as string= prop.Name dim a() as Introspection.AttributeInfo = prop.GetAttributes For each attr as Introspection.AttributeInfo in a if attr.Name = "DBColumn" then propName = attr.Value.StringValue end Next if propName = "" then Continue If d.HasKey(propName) then if prop.PropertyType.Name = "string" and d.value(propName) = nil then prop.Value(me) = chr(0) else prop.Value(me) = d.Value(propName) end end Next End Sub

This turns a recordset into a dictionary

[code]Function toDictionary(extends rs as RecordSet) As Dictionary
dim d as new Dictionary
For i as integer = 1 to rs.FieldCount
d.Value(rs.IdxField(i).Name) = rs.IdxField(i).Value
Next
return d

End Function
[/code]

Then I just call: object.load(rs.toDictionary)

Thanks, this all helped. I was just temporarily using the array to store a copy of the object for loading purposes before sending to the dictionary, but that wasn’t really significant in solving this. Using introspection to find the constructor and then using invoke from there solved it:

  dim hh(-1) as autodbClass=AutoDBClass.DatabaseItemTypes
  
  for i as integer=0 to UBound(hh)
    dim dd as Dictionary=hh(i).MyClassDictionary
    dd.Clear
    
    dim ti as Introspection.TypeInfo = Introspection.GetType(hh(i))
    Dim ci() As Introspection.ConstructorInfo = ti.GetConstructors
    dim constructorIndex as integer=-1
    
    for j as integer=0 to UBound(ci)
      Dim myParameters() as Introspection.ParameterInfo = ci(j).GetParameters
      For k as Integer=0 to Ubound(myParameters) 
        dim paramName as string = myParameters(k).ParameterType.name
        select case paramName
        case "RecordSet"
          constructorIndex=j
          exit
        end select
      Next
    next
    
    
    if dd<>nil and constructorIndex<>-1 then
      
      dim rs as RecordSet = db.db.SQLSelect("SELECT * FROM "+hh(i).MyClassName+" WHERE 1")
      
      if rs<>nil then
        while not rs.EOF
          
          dim vv(0) as Variant
          vv(0) = rs
          
          dim aa as AutoDBClass = ci(constructorIndex).Invoke(vv)
          dd.Value(aa.id) = aa
          
          rs.MoveNext
          
        wend
        rs.Close
      end if
      
    end if
  next