Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you cast to a type using the type name as a string?

Ok, I've thumped on this idea all day now, and I have reached the part where I admit I just flat out don't know. It's possible that what I'm doing is just stupid and there is a better way, but this is where my thinking has brought me.

I am attempting to use a generic method to load forms in WinForms:

protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new()
{
    // Do some stuff
}

The forms are loaded by a ToolStripMenuItem (either through the selection of the item or using the Open Windows menu item). They are lazy-loaded, so there are fields for the forms within the MDI parent, but they are null until they are needed. I have a common method used for ToolStripMenuItem_Click that handles all of the menu item clicks. The method has no real way of knowing which form is being called for except that the name of the ToolStripMenuItem matches a pattern chosen for the form class names they correspond to. So, using the name of the ToolStripMenuItem, I can divine the name of the type of form being requested and the name of the private field allocated to store the reference for that form.

Using that, I can either use a growing/contracting switch statement with hard-coded types and string matches to call method with the specific type set (undesirable), or I can use Reflection to get the field and create the instance of the type. The problem to me is, System.Activator.CreateInstance provides an ObjectHandler that can't be cast to the types that I need. Here is a snippet of what I have so far:

string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", "");
string formType = formName.Substring(1);

FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance);

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType);
}

this.LoadForm(ref formToLoad, false);
fi.SetValue(this, formToLoad);

I know the string name of the type that goes in for (????) but at compile-time I do not know the type because it changes. I have tried a bunch of ways to get this cast/instantiation to work, but none have been successful. I would very much like to know if it's possible to perform such a cast knowing the type only as a string. I tried using Type.GetType(string, string) to perform the cast, but the compiler didn't like it. If someone has a different idea on how to load the forms dynamically because I'm just doing it stupidly, please let me know about it.

like image 556
Joel Etherton Avatar asked Jan 28 '11 19:01

Joel Etherton


2 Answers

This problem is usually resolved by casting to a common base class or interface of all potential types.

In C# 4, you can also assign it to a dynamic variable to hold the return value and call arbitrary methods on it. The methods will be late bound. However, I prefer to stick to the former solution whenever possible.

like image 166
mmx Avatar answered Nov 15 '22 20:11

mmx


You'd be better off with the other overload that takes a Type and using e.g. Type.GetType(string).

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad =
      (FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType"));
}
like image 21
Andras Vass Avatar answered Nov 15 '22 21:11

Andras Vass