How to create a new object instance from a Type
可能在编译时并不总是知道对象的Type,但可能需要创建Type的实例。如何从Type中获取新的对象实例?
根System命名空间中的Activator类非常强大。 有很多重载可以将参数传递给构造函数等等。查看文档:
http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
或(新路径)
https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance
下面是一些简单的例子:
1 2 3
| ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);
ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType"); |
1
| ObjectType instance = (ObjectType)Activator.CreateInstance(objectType); |
Activator类有一个通用变量,使这变得更容易:
1
| ObjectType instance = Activator.CreateInstance<ObjectType>(); |
编译表达式是最好的方法!(以便性能在运行时重复创建实例)。
1 2 3 4 5
| static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
X x = YCreator(); |
统计(2012年):
1 2 3 4 5 6
| 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):
1 2 3 4 5 6
| 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):
1 2 3 4 5 6
| 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):
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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):
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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):
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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 |
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| 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 2 3 4 5 6 7 8 9 10 11
| public static object GetNewObject(Type t)
{
try
{
return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}
catch
{
return null;
}
} |
以下是包含在通用方法中的相同方法:
1 2 3 4 5 6 7 8 9 10 11
| public static T GetNewObject<T>()
{
try
{
return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
}
catch
{
return default(T);
}
} |
很简单。假设您的类名是Car,名称空间是Vehicles,然后将参数传递为Vehicles.Car,返回Car类型的对象。像这样,您可以动态地创建任何类的任何实例。
1 2 3 4 5
| public object GetInstance(string strNamesapace)
{
Type t = Type.GetType(strNamesapace);
return Activator.CreateInstance(t);
} |
如果您的完全限定名(在本例中为Vehicles.Car)在另一个程序集中,则Type.GetType将无效。在这种情况下,您将遍历所有程序集并找到Type。为此,您可以使用以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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;
} |
您可以通过调用上述方法来获取实例。
1
| object objClassInstance = GetInstance("Vehicles.Car"); |
如果这是为了在应用程序实例中经常调用的东西,那么编译和缓存动态代码要比使用Activator或ConstructorInfo.Invoke()快得多。动态编译的两个简单选项是编译的LINQ表达式或一些简单的IL操作码和DynamicMethod。不管是哪种方式,当你开始进入紧循环或多次调用时,差异是巨大的。
不使用反射:
1 2 3 4
| private T Create<T>() where T : class, new()
{
return new T();
} |
通用的T t = new T();不起作用吗?
如果您想使用默认的构造函数,那么使用前面介绍的System.Activator的解决方案可能是最方便的。但是,如果类型缺少默认构造函数或必须使用非默认构造函数,则可以选择使用反射或System.ComponentModel.TypeDescriptor。在反射的情况下,只知道类型名(及其名称空间)就足够了。 使用反射的示例:
1 2 3 4 5 6 7 8 9 10
| 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的示例:
1 2 3 4 5 6 7
| 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 }
); |
考虑到这个问题,当存在无参数的ctor时,激活器将工作。如果这是一个约束,请考虑使用
1
| System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject() |
我可以解决这个问题,因为我正在为任意类(使用默认构造函数)实现一个简单的CloneObject方法。 使用泛型方法,可以要求类型实现new()。
1 2 3 4 5 6 7 8 9 10 11
| 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 |
对于非泛型,假定类型具有默认的构造函数和catch如果没有,那就例外了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 |
1 2 3 4 5 6 7
| public AbstractType New
{
get
{
return (AbstractType) Activator.CreateInstance(GetType());
}
} |
|