关于c#:如何从Type创建新的对象实例

关于c#:如何从Type创建新的对象实例

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


推荐阅读