From: Captain Jack on
Kinda stumped on this one... I've got a generic class, defined like this:

Public Class GenClass(Of VType As IComparable)
Inherits BaseClass
[ ... ]
End Class

The class works just fine, no problems with it. I'm going to build a list of
objects of this type at run time, from a table in a database that
corresponds to the properties of GenClass. I've got a code in the database
that identifies what VType should be for each instance (for example, 1 would
be "String", 2 would be "Integer", and so on). So far, so good.

What I can't quite figure out is how to create an instance at run time of
the the generic class with a Type object, or anything else. I've looked at
the ConstructorInfo and Activator classes, and I'm either not seeing it,
looking in the wrong place, or trying to do it the wrong way.

I'm getting by right now with a Select Case statement, explicitly naming the
type, something like this (stripped of all but the essential code for this
example):

[... Function receives Rdr, a SqlClient.SqlDataReader ...]
Dim ObjectList As New List(Of BaseClass)
Do While Rdr.Read
Dim ValueCode As Integer = Rdr("ValueCode")
Select Case ValueCode
Case 1 : ObjectList.Add(New GenClass(Of String))
Case 2 : ObjectList.Add(New GenClass(Of Integer))
et cetera ...
End Select
Loop

This works, but seems clunky to me. However, I can't figure out a better way
of doing it. Anyone have any ideas they'd like to share? :-)

Thanks,
Jack



From: Armin Zingler on
Am 10.03.2010 22:10, schrieb Captain Jack:
> Kinda stumped on this one... I've got a generic class, defined like this:
>
> Public Class GenClass(Of VType As IComparable)
> Inherits BaseClass
> [ ... ]
> End Class
>
> The class works just fine, no problems with it. I'm going to build a list of
> objects of this type at run time, from a table in a database that
> corresponds to the properties of GenClass. I've got a code in the database
> that identifies what VType should be for each instance (for example, 1 would
> be "String", 2 would be "Integer", and so on). So far, so good.
>
> What I can't quite figure out is how to create an instance at run time of
> the the generic class with a Type object, or anything else. I've looked at
> the ConstructorInfo and Activator classes, and I'm either not seeing it,
> looking in the wrong place, or trying to do it the wrong way.
>
> I'm getting by right now with a Select Case statement, explicitly naming the
> type, something like this (stripped of all but the essential code for this
> example):
>
> [... Function receives Rdr, a SqlClient.SqlDataReader ...]
> Dim ObjectList As New List(Of BaseClass)
> Do While Rdr.Read
> Dim ValueCode As Integer = Rdr("ValueCode")
> Select Case ValueCode
> Case 1 : ObjectList.Add(New GenClass(Of String))
> Case 2 : ObjectList.Add(New GenClass(Of Integer))
> et cetera ...
> End Select
> Loop
>
> This works, but seems clunky to me. However, I can't figure out a better way
> of doing it. Anyone have any ideas they'd like to share? :-)


The association between the number (1, 2, etc) and the type is determined
by yourself, right? So I don't see another way but this Select Case statement.


--
Armin
From: Captain Jack on
"Armin Zingler" <az.nospam(a)freenet.de> wrote in message
news:OZoJ7QLwKHA.4196(a)TK2MSFTNGP02.phx.gbl...
> Am 10.03.2010 22:10, schrieb Captain Jack:

>> This works, but seems clunky to me. However, I can't figure out a better
>> way
>> of doing it. Anyone have any ideas they'd like to share? :-)
>
>
> The association between the number (1, 2, etc) and the type is determined
> by yourself, right? So I don't see another way but this Select Case
> statement.

Yeah... I was toying with the idea of maybe storing the name of the type in
the database though. So, instead of 1 or 2, I'd store String or Integer (or
maybe Int32 if that made more sense). I could get the type by name from the
current assembly, but I just don't have a clue how to create an instance of
the class, even if I have a Type variable set the proper value.

--
Jack


From: Mark Hurd on
"Captain Jack" <CaptainJack1024(a)comcast.net> wrote in message
news:_tKdnQt1w8KPlwXWnZ2dnUVZ_vidnZ2d(a)giganews.com...
> Kinda stumped on this one... I've got a generic class, defined like
> this:
>
> Public Class GenClass(Of VType As IComparable)
> Inherits BaseClass
> [ ... ]
> End Class
>
> The class works just fine, no problems with it. I'm going to build a
> list of objects of this type at run time, from a table in a database
> that corresponds to the properties of GenClass. I've got a code in the
> database that identifies what VType should be for each instance (for
> example, 1 would be "String", 2 would be "Integer", and so on). So
> far, so good.
>
> What I can't quite figure out is how to create an instance at run time
> of the the generic class with a Type object, or anything else. I've
> looked at the ConstructorInfo and Activator classes, and I'm either
> not seeing it, looking in the wrong place, or trying to do it the
> wrong way.
>
> I'm getting by right now with a Select Case statement, explicitly
> naming the type, something like this (stripped of all but the
> essential code for this example):
>
> [... Function receives Rdr, a SqlClient.SqlDataReader ...]
> Dim ObjectList As New List(Of BaseClass)
> Do While Rdr.Read
> Dim ValueCode As Integer = Rdr("ValueCode")
> Select Case ValueCode
> Case 1 : ObjectList.Add(New GenClass(Of String))
> Case 2 : ObjectList.Add(New GenClass(Of Integer))
> et cetera ...
> End Select
> Loop
>
> This works, but seems clunky to me. However, I can't figure out a
> better way of doing it. Anyone have any ideas they'd like to share?
> :-)
>
> Thanks,
> Jack

You need to know two things:

1. GetType can reference generic types by including Of with out a type
name.
E.g. GetType(GenClass(Of))

2. Type.MakeGenericType(T) is the runtime equivalent of (Of T).
E.g. GetType(GenClass(Of)).MakeGenericType(GetType(Integer)) Is
GetType(GenClass(Of Integer))
returns True.

So your Select Case code above becomes
' Map ValueCode to a type.
Dim T As Type
Select Case ValueCode
Case 1 : T = GetType(String)
Case 2 : T = GetType(Integer)
et cetera ...
End Select
' Create the generic type.
Dim G As Type = GetType(GenClass(Of)).MakeGenericType(T)
' Create the object and add to the list.
ObjectList.Add(Activator.CreateInstance(G))

--
Regards,
Mark Hurd, B.Sc.(Ma.) (Hons.)

FYI My sample code as used in Snippet Compiler with Option Infer On: (As
you can see I had some trouble getting the Object() to be accepted as an
argument to the List(Of) constructor.)
Sub RunSnippet()

WL(GetType(List(Of)).MakeGenericType(GetType(Integer)) Is
GetType(List(Of Int32)))


ML(GetType(Integer), 1, 2, 3)

ML(GetType(String), "A", "B", "C")

End Sub

Sub ML(Type As Type, ParamArray Args As Object())

Dim l = GetType(List(Of)).MakeGenericType(Type)

'Dim a = GetType(IEnumerable(Of)).MakeGenericType(Type)

Dim m = GetType(Enumerable).GetMethod("Cast").MakeGenericMethod(Type)

Dim i = Activator.CreateInstance(l, m.Invoke(Nothing, New
Object(){Args}))

WL(i)

For Each e In i

WL(e)

Next

End Sub



Produces this output:

True
System.Collections.Generic.List`1[System.Int32]
1
2
3
System.Collections.Generic.List`1[System.String]
A
B
C


From: Captain Jack on
"Mark Hurd" <markhurd(a)ozemail.com.au> wrote in message
news:%23Ofin8ewKHA.5132(a)TK2MSFTNGP05.phx.gbl...
> "Captain Jack" <CaptainJack1024(a)comcast.net> wrote in message
> news:_tKdnQt1w8KPlwXWnZ2dnUVZ_vidnZ2d(a)giganews.com...
>> Kinda stumped on this one... I've got a generic class, defined like this:
>>
>> [ ... ]
>
> You need to know two things:
>
> 1. GetType can reference generic types by including Of with out a type
> name.
> E.g. GetType(GenClass(Of))
>
> 2. Type.MakeGenericType(T) is the runtime equivalent of (Of T).
> E.g. GetType(GenClass(Of)).MakeGenericType(GetType(Integer)) Is
> GetType(GenClass(Of Integer))
> returns True.

That was indeed what I was looking for, thank you. I may have painted myself
into a bit of a corner, though, so I'm reviewing the whole thing. The main
reason I'm using a generic class in this case is so I that I can have a
class that doesn't have to go through all kinds of hoops to properly compare
a property found at run time in another, unrelated object to a constant
pre-loaded from the database, as well as nicely enforcing the correct type
in each different element of my list. That does work, no problems, and the
code above makes the process of instantiating the classes much more elegant
(if I switch the numeric code in the database to a string that names the
type, even better). By having the generic class descend from a non-generic
class, it's easire to define the list object, which was a nice plus.

Each of the generic classes has a property that returns a value (the
property is brilliantly named "Value") of the type defined in the generic
part. Since the property "Value" is defined in the generic class, of course,
when I pull an item from the list as the BaseClass, "Value" isn't
recognized. I could cast the item to type Object, of course, but that shoots
elegance right in the gut and leaves it for dead by the side of the road.
Shadowing a dummy property in the base class doesn't seem to get me anywhere
either. I'm sure there's some way around this, ultimately, but I'm beginning
to think I over-complicated this one in my neverending quest for "beautiful"
code.

--
Jack