tags:

views:

98

answers:

3

Is there a way to dynamically create an object using a string as the class name?

I've been off VB for several years now, but to solve a problem in another language, I'm forced to develop a wrapper in this one. I have a factory method to dynamically create and return an object of a type based on input from elsewhere. The provided input is meant to be the class name from which to create an object from. Normal syntax means that the entire class has to be explicitly spelled out. To do it this way, there could literally be hundreds of if/then's or cases to handle all the available class/object choices within the referenced libs:

If c_name = "Button" then obj = new System.Windows.Forms.Button
If c_name = "Form" then obj = new System.Windows.Forms.Form
....

I'm hoping instead to reduce all this case handling to a single line: IE...

my_class_name = "whateverclass"
obj = new System.Windows.Forms.my_class_name()

In PHP, this is handled like so...

$my_class_name = "whateverclass";
$obj = new $my_class_name();

Edit: Looking at some of the answers, I think I'm in way over my head here. I did manage to get it working using this CreateInstance method variation of the Assembly class, even though I'm more interested in this variation giving more options, including supplying construct parameters...

my_type_name = "System.Windows.Forms.Button"
asmb_name = "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
button1 = Reflection.Assembly.Load(asmb_name).CreateInstance(my_type_name)

In other words, it takes a method to do this, and not any inherent language syntax? This Activator variation also worked when the full assembly string and class path is used. I'm suspicious CreateInstance may not have the full ability to let me treat objects as if they were called normally, ie obj = new System.Windows.Forms.Button. This is why I can't use simply CreateObject. If there is no natural language feature allowing you to substitute a class name for a string, does anyone have any insight into what sort of limitations I can expect from using CreateInstance?

Also, is there even a difference between basic Activator.CreateInstance (after Unwrap) and Assembly.CreateInstance methods?

A: 

Take a look at the Activator.CreateInstance(Type) method.

If your input is the name of a class you should be able do this:

Dim obj As Object = Activator.CreateInstance(GetType("Name_Of_Your_Class")) 

You'll have to fiddle with the GetType call to make sure you give it enough information but for most cases just the name of the class should work.

jwsample
I believe Gettype() expects a type, so will throw an error. Type.Gettype("System.Windows.Forms.Button") returns null.
bob-the-destroyer
My vb is a bit rusty but this should definitely work. Not sure why you are getting null, but here't the GetType documentation for passing a string in: http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx
jwsample
The example is pretty much what I was originally using, except with the Gettype method of Type instead. I believe this leaves me stuck figuring a way to first hunt down the System.Windows.Forms.Button type.
bob-the-destroyer
+1  A: 

I'm pretty sure Activator is used for remoting. What you want to do is use reflection to get the constor and invoke it here's an example http://www.eggheadcafe.com/articles/20050717.asp

EDIT: I was misguided about Activator until jwsample corrected me.

I think the problem your having is that your assembly is the one that GetType is using to try and find Button. You need to call it from the right assembly.

This should do it

Dim asm As System.Reflection.Assembly = System.Reflection.Assembly.LoadWithPartialName("System.Windows.Forms")


Dim obj As Object = Activator.CreateInstance(asm.GetType("System.Windows.Forms.Button"))
Conrad Frix
Wow, you changed the entire contents of your post with that edit and now my previous comment looks like the ravings of a mad man.
jwsample
Sorry about that. I put it back.
Conrad Frix
Good leads. Hate to nitpick, but with Activator.CreateInstance, because it returns a handle, it requires that you call the handle Unwrap method on it to finish creating the object. Also, the Assembly.LoadWithPartialName method is obsolete according to http://msdn.microsoft.com/en-us/library/system.reflection.assembly.loadwithpartialname.aspx. Shame, because I really could use it instead of messing around trying to find versions, cultures, and keys.
bob-the-destroyer
+2  A: 

This will likely do what you want / tested working; switch the type comment at the top to see.

Imports System.Reflection

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    '            Dim fullyQualifiedClassName as String = "System.Windows.Forms.TextBox"
    Dim fullyQualifiedClassName As String = "System.Windows.Forms.Button"
    Dim o = fetchInstance(fullyQualifiedClassName)
    ' sometime later where you can narrow down the type or interface...
    Dim b = CType(o, Control)
    b.Text = "test"
    b.Top = 10
    b.Left = 10
    Controls.Add(b)
End Sub

Private Function fetchInstance(ByVal fullyQualifiedClassName As String) As Object
    Dim nspc As String = fullyQualifiedClassName.Substring(0, fullyQualifiedClassName.LastIndexOf("."c))
    Dim o As Object = Nothing
    Try
        For Each ay In Assembly.GetExecutingAssembly().GetReferencedAssemblies()
            If (ay.Name = nspc) Then
                o = Assembly.Load(ay).CreateInstance(fullyQualifiedClassName)
                Exit For
            End If
        Next
    Catch
    End Try
    Return o
End Function
Tahbaza
Nice. `Form1_Load()` will actually be handled through another language for me via COM. The line `o = Assembly.Load(ay).CreateInstance(fullyQualifiedClassName)` should do it.
bob-the-destroyer