如何从类型创建新的对象实例


748

可能并非总是Type在编译时就知道对象的,但是可能需要创建的实例Type

如何从中获取新的对象实例Type

Answers:


894

ActivatorSystem名称空间中的类非常强大。

将参数传递给构造函数等的重载很多。在以下位置查看文档:

http://msdn.microsoft.com/zh-CN/library/system.activator.createinstance.aspx

或(新路径)

https://docs.microsoft.com/zh-cn/dotnet/api/system.activator.createinstance

以下是一些简单的示例:

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

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

20
很高兴终于找到了这个,但是第二次调用并不完全正确,缺少引号并且颠倒了参数,应该是:ObjectType instance =(ObjectType)Activator.CreateInstance(“ MyAssembly”,“ MyNamespace.ObjectType”);
kevinc

9
您需要调用“ Unwrap()”来获取所需的对象的实际类型:ConcreteType instance =(ConcreteType)Activator.CreateInstance(objectType).Unwrap();
ΕГИІИО

4
ObjectType instanceOP的条件“在编译时可能并不总是知道对象的类型” 如何匹配?:P
Martin Schneider

@ MA-Maddin好吧,object instance = Activator.CreateInstance(...);
BrainSlugs83

1
有人知道如何在.NET Core中执行此操作吗?Unwrap方法在对象上不可用。
贾斯汀

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

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

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

8
@Kevin当然。这样的操作不能在静态类型的语言中工作,因为它没有意义。您不能在未知类型的对象上调用方法。同时(自编写此答案以来,=)C#得到了确实允许这种构造的构造,但对于大多数目的,此答案仍然涵盖了该dynamic构造。
康拉德·鲁道夫2012年

1
@KonradRudolph不太正确。c#首先确实允许您在运行时创建新类型。您不能用静态安全的方式对其进行调用。是的,你是正确的一半。但实际上,在运行时加载程序集时需要使用它,这意味着在编译时不知道类型。如果您不能这样做,C#将受到严格限制。我的意思是您只是亲自证明了这一点:采取类型实例的Activator方法还有什么用?当MS编写Activator类时,他们对用户将来会写的任何未来类型都不了解。
AnorZaken

1
@AnorZaken我的评论没有提到在运行时创建类型。当然可以这样做,但是不能在同一上下文中静态使用它们(当然,您可以托管完整的静态编译程序)。我的评论就是这么说。
康拉德·鲁道夫

@KonradRudolph抱歉,将“这样的操作”误解为实例化仅在运行时才知道的类型;而不是使用运行时类型作为泛型类型参数。
AnorZaken

1
@AnorZaken-从技术上讲,您既可以在运行时创建新类型,也可以以静态安全的方式在其上调用方法,如果您的新类型实现了已知的接口或继承了已知的基类。-这些方法中的任何一种都会为您的运行时创建的对象提供静态合同。
BrainSlugs83

132

编译表达式是最好的方法!(为了提高性能,可以在运行时重复创建实例)。

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

统计信息(2019,x64 / .NET 4.8):

Iterations: 5000000
No args
00:00:00.3287835, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.3122015, Activator.CreateInstance(Type type)
00:00:00.8035712, ConstructorInfo.Invoke
00:00:00.0692854, Compiled expression
00:00:00.0662223, Compiled expression (type)
00:00:00.0337862, new
Single arg
00:00:03.8081959, Activator.CreateInstance(Type type)
00:00:01.2507642, ConstructorInfo.Invoke
00:00:00.0671756, Compiled expression
00:00:00.0301489, new

统计信息(2019,x64 / .NET Core 3.0):

Iterations: 5000000
No args
00:00:00.3226895, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.2786803, Activator.CreateInstance(Type type)
00:00:00.6183554, ConstructorInfo.Invoke
00:00:00.0483217, Compiled expression
00:00:00.0485119, Compiled expression (type)
00:00:00.0434534, new
Single arg
00:00:03.4389401, Activator.CreateInstance(Type type)
00:00:01.0803609, ConstructorInfo.Invoke
00:00:00.0554756, Compiled expression
00:00:00.0462232, 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) {}
}

18
+1所有统计信息!目前,我真的不需要这种表现,但仍然很有趣。:)
AnorZaken

1
另外还有TypeDescriptor.CreateInstance(请参阅stackoverflow.com/a/17797389/1242),如果与TypeDescriptor.AddProvider一起使用,它可以更快一些
Lars Truijens

2
当您X在运行时不知道什么类型时,这仍然有用吗?
ajeh

1
@ajeh是的。将typeof(T)更改为Type.GetType(..)。
Serj-Tm

3
@ Serj-Tm不,如果类型X是运行时,则将不起作用Type
NetMage

47

此问题的一种实现是尝试调用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
异常驱动编程?当您可以简单地考虑类型以确定构造函数时,这似乎是一个非常糟糕的实现。
Firoso

16

非常简单。假设您的类名是Car,名称空间是Vehicles,然后传递参数,Vehicles.Car该参数返回type的对象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");

在第二种情况下(外部装配),您可以将“ Vehicles.Car,OtherAssembly”传递给第一种方法,它将起作用。显然,OtherAssembly是其所在大会的名称
。– danmiser

2
@danmiser这需要对程序集名称进行硬编码。为了实现灵活性,我正在检查null,并且代码以动态方式工作:)
Sarath Avanavu

14

如果这是为了在应用程序实例中被大量调用,那么与使用activator或相比,编译和缓存动态代码要快得多ConstructorInfo.Invoke()。动态编译的两个简单选项是编译的Linq表达式或一些简单的IL操作码和DynamicMethod。无论哪种方式,当您开始陷入紧密的循环或多次调用时,两者之间的差异都是巨大的。



10

如果要使用默认构造函数,那么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 }
    );

args[]正是我来到这个问题中寻找的,谢谢!
乍得

10

不使用反射:

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

5
这有什么用?您必须已经知道要调用该方法的类型,如果知道该类型,则可以在没有特殊方法的情况下构造它。
Kyle Delaney

因此,T在运行时可能会有所不同。如果使用派生类型,则很有用。

一个新的T(); 如果T不是具有无参数构造函数的引用类型,则将失败,此方法使用约束来确保T是引用类型并具有构造函数。

3
T在运行时如何变化?您不是必须在设计时就知道T才能调用Create <>吗?
凯尔·德莱尼

如果您在工厂中使用通用类和接口,则应实例化实现接口的类型可能会有所不同。

8

鉴于此问题,当存在无参数的ctor时,激活器将起作用。如果这是一个约束,请考虑使用

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()


4

我可以解决这个问题,因为我想为任意类实现一个简单的CloneObject方法(使用默认构造函数)

使用泛型方法,您可以要求该类型实现New()。

Public Function CloneObject(Of T As New)(ByVal src As T) As T
    Dim result As T = Nothing
    Dim cloneable = TryCast(src, ICloneable)
    If cloneable IsNot Nothing Then
        result = cloneable.Clone()
    Else
        result = New T
        CopySimpleProperties(src, result, Nothing, "clone")
    End If
    Return result
End Function

对于非泛型,假定类型具有默认构造函数,如果没有则捕获异常。

Public Function CloneObject(ByVal src As Object) As Object
    Dim result As Object = Nothing
    Dim cloneable As ICloneable
    Try
        cloneable = TryCast(src, ICloneable)
        If cloneable IsNot Nothing Then
            result = cloneable.Clone()
        Else
            result = Activator.CreateInstance(src.GetType())
            CopySimpleProperties(src, result, Nothing, "clone")
        End If
    Catch ex As Exception
        Trace.WriteLine("!!! CloneObject(): " & ex.Message)
    End Try
    Return result
End Function
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.