Instantiate an object with a runtime-determined type

I'm in a situation where I'd like to instantiate an object of a type that will be determined at runtime. I also need to perform an explicit cast to that type.

Something like this:

static void castTest(myEnum val)
{
//Call a native function that returns a pointer to a structure
IntPtr = someNativeFunction(..params..);


//determine the type of the structure based on the enum value
Type structType = getTypeFromEnum(val);


structType myStruct = (structType)Marshal.PtrToStructure(IntPtr, structType);
}

This is obviously not valid code, but I hope it conveys the essence of what I'm trying to do. The method I'm actually working on will have to perform the marshaling operation on ~35 different types. I have several other methods that will need to do something similar with the same set of types. So, I'd like to isolate the type-determining logic from these methods so that I only need to write it once, and so that the methods stay clean and readable.

I must admit to being a total novice at design. Could anyone suggest a good approach to this problem? I suspect there might be an appropriate design pattern that I'm unaware of.

99820 次浏览

I think you're looking for Activator.CreateInstance

There are several ways you can create an object of a certain type on the fly, one is:

// determine type here
var type = typeof(MyClass);


// create an object of the type
var obj = (MyClass)Activator.CreateInstance(type);

And you'll get an instance of MyClass in obj.

Another way is to use reflection:

// get type information
var type = typeof(MyClass);


// get public constructors
var ctors = type.GetConstructors(BindingFlags.Public);


// invoke the first public constructor with no parameters.
var obj = ctors[0].Invoke(new object[] { });

And from one of ConstructorInfo returned, you can "Invoke()" it with arguments and get back an instance of the class as if you've used a "new" operator.

You can mostly do what you're describing, but since you don't know the type at compile-time, you'll have to keep the instance loosely-typed; check its type at each point you use it, and cast it appropriately then (this will not be necessary with c# 4.0, which supports dynamics):

Type type = CustomGetTypeMethod();
var obj = Activator.CreateInstance(type);


...




if(obj is MyCustomType)
{
((MyCustomType)obj).Property1;
}
else if (obj is MyOtherCustomType)
{
((MyOtherCustomType)obj).Property2;
}

Creating an instance of a run-time determined Type is easy, using Activator.CreateInstance, as others have mentioned. However, casting it, as you do in your example on the Marshal.PtrToStructure line is not possible, as the type has to be known at compile time for casting. Also, note that Activator.CreateInstance can not be used in conjunction with an IntPtr.

If your types have a common base class (other than Object), you can cast it to said base type and call functions on that. Otherwise, calling functions will only be possible using reflection.

So either:

static void castTest(myEnum val)
{
//Call a native function that returns a pointer to a structure
IntPtr val = someNativeFunction(..params..);


//determine the type of the structure based on the enum value
Type structType = getTypeFromEnum(val);


BaseClass myStruct = (BaseClass)Marshal.PtrToStructure(IntPtr, structType);
myStruct.SomeFunctionDeclaredInBaseClass();
}

Or:

static void castTest(myEnum val)
{
//Call a native function that returns a pointer to a structure
IntPtr val = someNativeFunction(..params..);


//determine the type of the structure based on the enum value
Type structType = getTypeFromEnum(val);


object myStruct = Marshal.PtrToStructure(IntPtr, structType);
MemberInfo[] function = FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance,
(MemberFilter)delegate(MemberInfo info, object filter)
{
return info.Name == filter.ToString();
}, "SomeFunction");
if (mi.Length > 0 && mi[0] is MethodInfo)
((MethodInfo)mi[0]).Invoke(myStruct, ..params..);
}

You could go dynamic:

using System;


namespace TypeCaster
{
class Program
{
internal static void Main(string[] args)
{
Parent p = new Parent() { name = "I am the parent", type = "TypeCaster.ChildA" };
dynamic a = Convert.ChangeType(new ChildA(p.name), Type.GetType(p.type));
Console.WriteLine(a.Name);


p.type = "TypeCaster.ChildB";
dynamic b = Convert.ChangeType(new ChildB(p.name), Type.GetType(p.type));
Console.WriteLine(b.Name);
}
}


internal class Parent
{
internal string type { get; set; }
internal string name { get; set; }


internal Parent() { }
}


internal class ChildA : Parent
{
internal ChildA(string name)
{
base.name = name + " in A";
}


public string Name
{
get { return base.name; }
}
}


internal class ChildB : Parent
{
internal ChildB(string name)
{
base.name = name + " in B";
}


public string Name
{
get { return base.name; }
}
}
}
 methodName = NwSheet.Cells[rCnt1, cCnt1 - 2].Value2;
Type nameSpace=typeof(ReadExcel);
Type metdType = Type.GetType(nameSpace.Namespace + "." + methodName);
//ConstructorInfo magicConstructor = metdType.GetConstructor(Type.EmptyTypes);
//object magicClassObject = magicConstructor.Invoke(new object[] { });
object magicClassObject = Activator.CreateInstance(metdType);
MethodInfo mthInfo = metdType.GetMethod("fn_"+methodName);
StaticVariable.dtReadData.Clear();
for (iCnt = cCnt1 + 4; iCnt <= ShtRange.Columns.Count; iCnt++)
{
temp = NwSheet.Cells[1, iCnt].Value2;
StaticVariable.dtReadData.Add(temp.Trim(), Convert.ToString(NwSheet.Cells[rCnt1, iCnt].Value2));
}




//if (Convert.ToString(NwSheet.Cells[rCnt1, cCnt1 - 2].Value2) == "fn_AddNum" || Convert.ToString(NwSheet.Cells[rCnt1, cCnt1 - 2].Value2) == "fn_SubNum")
//{
//    //StaticVariable.intParam1 = Convert.ToInt32(NwSheet.Cells[rCnt1, cCnt1 + 4].Value2);
//    //StaticVariable.intParam2 = Convert.ToInt32(NwSheet.Cells[rCnt1, cCnt1 + 5].Value2);
//    object[] mParam1 = new object[] { Convert.ToInt32(StaticVariable.dtReadData["InParam1"]), Convert.ToInt32(StaticVariable.dtReadData["InParam2"]) };
//    object result = mthInfo.Invoke(this, mParam1);
//    StaticVariable.intOutParam1 = Convert.ToInt32(result);
//    NwSheet.Cells[rCnt1, cCnt1 + 2].Value2 = Convert.ToString(StaticVariable.intOutParam1) != "" ? Convert.ToString(StaticVariable.intOutParam1) : String.Empty;
//}


//else
//{
object[] mParam = new object[] { };
mthInfo.Invoke(magicClassObject, mParam);