使用运行时确定的类型实例化一个对象

82

我现在遇到这样的情况:我需要在运行时确定对象的类型并且需要对该类型进行显式转换。类似于这样:

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);
}

显然这不是有效的代码,但我希望它传达了我想做的事情的本质。我实际上要处理大约35种不同类型的编组操作。我有其他几个方法也需要使用相同的类型集合执行类似的操作。因此,我想将确定类型的逻辑与这些方法隔离开来,以便我只需要编写一次,而且方法保持干净和可读。

我必须承认自己在设计方面是一个彻头彻尾的新手。是否有人能够建议一个好的解决方法?我怀疑可能有一个适当的设计模式我还不知道。


1
也许 http://msdn.microsoft.com/en-us/library/system.activator.aspx 可以帮助您?它通常用于实例化在运行时才知道类型的对象。虽然可以使用常规反射来完成,但有点麻烦。不过,我对您的设计没有任何想法,所以我不会给出答案。 - Skurmedel
1
可能是 从类型获取新对象实例 的重复问题。 - nawfal
6个回答

152

有几种方法可以即时创建一个特定类型的对象,其中一种是:

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

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

你会在obj中得到一个MyClass的实例。

另一种方法是使用反射:

// 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[] { });

从返回的ConstructorInfo之一中,您可以使用参数"Invoke()"并像使用"new"运算符一样获取该类的实例。


34
这并不能完全回答这个问题。编译时他不知道它将是MyClass,还是HisClass或者HerClass(由运行时确定的类型)。 - Bitterblue
4
请注意,在问题中OP包含了Type structType = getTypeFromEnum(val);这一行代码。我只是添加一个示例代码,以演示应该先获取Type实例。 - chakrit
1
不确定你是否明白:我批评了你回答中的类型转换部分。Rex M的答案提供了一个完整的工作解决方案,并引用了另一种使用动态的方法。 - Bitterblue
2
@mini-me 这只是一个例子。OP正在寻找一个API来完成这个任务。代码足以回答问题。我不能让所有东西都按照OP的意愿编译,这对OP也没有什么帮助。在现实中,大多数情况下,您至少会知道并且希望对象基于某些基本类型或接口,而几乎永远不会完全动态或直接使用纯粹的“Object”,因为它们相当无用(或者不需要运行时反射来使用)。 - chakrit
3
我使用了 inputObj.GetType() 而不是 typeof(MyClass)。这提供了类型,但可能无法用于强制转换。在我的情况下,我有一个适当的父类,足以进行强制转换。 - Zarepheth
这需要在编译时知道类类型是什么,而 OP 表示他们不知道。Rex M 的答案更好。 - John Stock

18

你基本上可以做你描述的事情,但由于在编译时你不知道类型,所以必须保持实例的松散类型; 在每次使用它时检查其类型,并进行适当的转换(这在c# 4.0中将不再需要,因为它支持动态):

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

...


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

11

我认为您正在寻找Activator.CreateInstance



3
是的,我也是。我使用了这个帮助链接:https://dev59.com/hXVD5IYBdhLWcg3wXaid - Valamas

6
使用Activator.CreateInstance可以轻松地创建一个运行时确定的类型的实例,正如其他人已经提到的那样。但是,在您在Marshal.PtrToStructure行中所做的示例中进行类型转换是不可能的,因为必须在编译时知道类型才能强制转换。此外,请注意,Activator.CreateInstance不能与IntPtr一起使用。
如果您的类型具有一个公共基类(除了Object),则可以将其强制转换为该基类型并调用该函数。否则,仅能通过反射来调用函数。
因此,要么:
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();
}

或者:

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..);
}

+1. @Aistina的更有用的想法是定义一个基类或接口,并针对该基类进行所需的任何工作,而不仅仅是创建一个运行时确定类型的实例。 - Evan

1

您可以选择动态方式:

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; }
        }
    }
}

-7
 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接