如何从Type创建新的对象实例

tags2k 624 .net c# reflection performance types

人们可能并不总是Type在编译时知道对象,但可能需要创建一个实例Type.你如何从一个新的对象实例Type

Karl Seguin.. 777

ActivatorSystem命名空间中的类非常强大.

将参数传递给构造函数等有很多重载.查看以下文档:

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

或(新路径)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

以下是一些简单的例子:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");

  • 很高兴终于找到了这个,但第二次调用并不完全正确,缺少引号和parms反转,应该是:ObjectType instance =(ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType"); (18认同)
  • 您需要调用'Unwrap()'来获取所需的实际对象类型:ConcreteType instance =(ConcreteType)Activator.CreateInstance(objectType).Unwrap(); (8认同)

Konrad Rudol.. 121

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

Activator班有一个通用的变种,使这是一个更容易一些:

ObjectType instance = Activator.CreateInstance<ObjectType>();

  • 除此之外不适用于运行时`Type t`. (11认同)
  • @Kevin当然.这样的操作*不能用静态类型的语言工作,因为它没有意义.您无法在未知类型的对象上调用方法.在此期间(=自写这个答案以来)C#已经得到了`dynamic`结构**允许这样的结构但是在大多数情况下这个答案仍然涵盖了它. (5认同)

Serj-Tm.. 98

编译表达式是最好的方法!(用于在运行时重复创建实例的性能).

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();

X x = YCreator();

统计(2012年):

    Iterations: 5000000
    00:00:00.8481762, Activator.CreateInstance(string, string)
    00:00:00.8416930, Activator.CreateInstance(type)
    00:00:06.6236752, ConstructorInfo.Invoke
    00:00:00.1776255, Compiled expression
    00:00:00.0462197, new

统计(2015年,.net 4.5,x64):

    Iterations: 5000000
    00:00:00.2659981, Activator.CreateInstance(string, string)
    00:00:00.2603770, Activator.CreateInstance(type)
    00:00:00.7478936, ConstructorInfo.Invoke
    00:00:00.0700757, Compiled expression
    00:00:00.0286710, new

统计(2015年,.net 4.5,x86):

    Iterations: 5000000
    00:00:00.3541501, Activator.CreateInstance(string, string)
    00:00:00.3686861, Activator.CreateInstance(type)
    00:00:00.9492354, ConstructorInfo.Invoke
    00:00:00.0719072, Compiled expression
    00:00:00.0229387, new

统计(2017年,LINQPad 5.22.02/x64/.NET 4.6):

    Iterations: 5000000
    No args
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName)
    00:00:00.3500748, Activator.CreateInstance(Type type)
    00:00:01.0100714, ConstructorInfo.Invoke
    00:00:00.1375767, Compiled expression
    00:00:00.1337920, Compiled expression (type)
    00:00:00.0593664, new
    Single arg
    00:00:03.9300630, Activator.CreateInstance(Type type)
    00:00:01.3881770, ConstructorInfo.Invoke
    00:00:00.1425534, Compiled expression
    00:00:00.0717409, new

完整代码:

static X CreateY_New()
{
    return new Y();
}

static X CreateY_New_Arg(int z)
{
    return new Y(z);
}

static X CreateY_CreateInstance()
{
    return (X)Activator.CreateInstance(typeof(Y));
}

static X CreateY_CreateInstance_String()
{
    return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}

static X CreateY_CreateInstance_Arg(int z)
{
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, });
}

private static readonly System.Reflection.ConstructorInfo YConstructor =
    typeof(Y).GetConstructor(Type.EmptyTypes);
private static readonly object[] Empty = new object[] { };
static X CreateY_Invoke()
{
    return (X)YConstructor.Invoke(Empty);
}

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg =
    typeof(Y).GetConstructor(new[] { typeof(int), });
static X CreateY_Invoke_Arg(int z)
{
    return (X)YConstructor_Arg.Invoke(new object[] { z, });
}

private static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
static X CreateY_CompiledExpression()
{
    return YCreator();
}

private static readonly Func<X> YCreator_Type = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y))
).Compile();
static X CreateY_CompiledExpression_Type()
{
    return YCreator_Type();
}

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z");
private static readonly Func<int, X> YCreator_Arg = Expression.Lambda<Func<int, X>>(
   Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }),
   YCreator_Arg_Param
).Compile();
static X CreateY_CompiledExpression_Arg(int z)
{
    return YCreator_Arg(z);
}

static void Main(string[] args)
{
    const int iterations = 5000000;

    Console.WriteLine("Iterations: {0}", iterations);

    Console.WriteLine("No args");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke},
        new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression},
        new {Name = "Compiled expression (type)", Creator = (Func<X>)CreateY_CompiledExpression_Type},
        new {Name = "new", Creator = (Func<X>)CreateY_New},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator().Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator();
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }

    Console.WriteLine("Single arg");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<int, X>)CreateY_CreateInstance_Arg},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<int, X>)CreateY_Invoke_Arg},
        new {Name = "Compiled expression", Creator = (Func<int, X>)CreateY_CompiledExpression_Arg},
        new {Name = "new", Creator = (Func<int, X>)CreateY_New_Arg},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator(i).Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator(i);
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }
}

public class X
{
  public X() { }
  public X(int z) { this.Z = z; }
  public int Z;
}

public class Y : X
{
    public Y() {}
    public Y(int z) : base(z) {}
}

  • 所有统计数据均为+1!我此刻并不需要这种表现,但仍然非常有趣.:) (11认同)

tags2k.. 41

此问题的一个实现是尝试调用Type的无参数构造函数:

public static object GetNewObject(Type t)
{
    try
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return null;
    }
}

这是一种通用方法中包含的相同方法:

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}

  • 异常驱动编程?当您可以简单地反映类型以确定构造函数时,这似乎是一个非常糟糕的实现. (15认同)

Thomas G. Ma.. 12

如果这是在应用程序实例中被大量调用的东西,那么编译和缓存动态代码而不是使用激活器或更快的速度要快得多ConstructorInfo.Invoke().动态编译的两个简单选项是编译Linq表达式或一些简单的IL操作码和DynamicMethod.无论哪种方式,当您开始进入紧密循环或多个呼叫时,差异是巨大的.


Sarath Avana.. 12

它非常简单.假设您的类名是Car和命名空间Vehicles,然后传递Vehicles.Car返回类型对象的参数Car.像这样,您可以动态创建任何类的任何实例.

public object GetInstance(string strNamesapace)
{         
     Type t = Type.GetType(strNamesapace); 
     return  Activator.CreateInstance(t);         
}

如果您的完全限定名称(即,Vehicles.Car在这种情况下)在另一个程序集中,Type.GetType则将为null.在这种情况下,你循环遍历所有程序集并找到Type.为此,您可以使用以下代码

public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

您可以通过调用上面的方法来获取实例.

object objClassInstance = GetInstance("Vehicles.Car");


用户甲.. 9

不使用反射:

private T Create<T>() where T : class, new()
{
    return new T();
}

  • 这有用吗?您必须知道已经调用该方法的类型,如果您知道类型,则可以在没有特殊方法的情况下构造它. (2认同)

Brady Moritz.. 7

通用T t = new T();工作不会吗?

  • 实际上,它将在泛型类/方法中,但不适用于给定的"类型". (8认同)

BSharp.. 7

如果您想使用默认构造函数,那么System.Activator前面介绍的解决方案可能是最方便的.但是,如果类型缺少默认构造函数或者您必须使用非默认构造函数,则选项是使用反射或System.ComponentModel.TypeDescriptor.在反射的情况下,只知道类型名称(及其名称空间)就足够了.

使用反射的示例:

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
        typeName: objectType.FulName, // string including namespace of the type
        ignoreCase: false,
        bindingAttr: BindingFlags.Default,
        binder: null,  // use default binder
        args: new object[] { args, to, constructor },
        culture: null, // use CultureInfo from current thread
        activationAttributes: null
    );

示例使用TypeDescriptor:

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
        provider: null, // use standard type description provider, which uses reflection
        objectType: objectType,
        argTypes: new Type[] { types, of, args },
        args: new object[] { args, to, constructor }
    );


Thulani Chiv.. 5

鉴于此问题,当存在无参数的ctor时,Activator将工作.如果这是一个约束考虑使用

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()