Получить экземпляр нового объекта из типа


541

Возможно, не всегда можно узнать тип объекта во время компиляции, но может потребоваться создать экземпляр типа. Как получить экземпляр нового объекта из типа?

685

Activator класс в корне System namespace довольно мощный.

Есть много перегрузок для передачи параметров конструктору и тому подобное. Проверьте документацию по адресу:

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"); 
+16

Рад, что наконец-то нашел это, но второй вызов не совсем прав, отсутствие цитаты и парм отменено, должно быть: ObjectType instance = (ObjectType) Activator.CreateInstance («MyAssembly», «MyNamespace.ObjectType»); 03 июн. 132013-06-03 10:56:15

+8

Вам нужно позвонить 'Unwrap()', чтобы получить нужный тип объекта: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap(); 13 мар. 142014-03-13 15:08:47


34

Одна реализация этой проблемы заключается в попытке вызвать без параметров конструктор Тип:

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

Программирование на основе исключений? Это кажется очень плохой реализацией, когда вы можете просто отразить тип для определения конструкторов. 22 мар. 142014-03-22 21:57:41


11

Если это для чего-то, что будет называться много в экземпляре приложения, намного быстрее выполнить компиляцию и кеширование динамического кода вместо использования активатора или ConstructorInfo.Invoke(). Два простых варианта динамической компиляции составляются Linq Expressions или некоторые простые IL opcodes and DynamicMethod. В любом случае разница огромна, когда вы начинаете получать плотные петли или несколько вызовов.

  0

Ссылка «IL opcodes и DynamicMethod» мертва. 30 апр. 172017-04-30 17:38:22


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

Activator класс имеет общий вариант, который делает это немного проще:

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

Кроме того, это не работает во время выполнения 'Тип t'. 05 апр. 122012-04-05 06:58:42

+3

@ Кевин Конечно. Такая операция * не может * работать на статически типизированном языке, потому что это не имеет смысла. Вы не можете вызывать методы на объекте неизвестного типа.Тем временем (= после написания ответа) C# имеет конструкцию 'dynamic', которая * позволяет * разрешать такие конструкции, но для большинства целей этот ответ по-прежнему ее охватывает. 07 апр. 122012-04-07 15:47:59

  0

@ KonradRudolph Не совсем верно. Первый из C# * позволяет * создавать новые типы во время выполнения. Вы просто не можете ничего на них называть * статически безопасным способом *. Так что да, ты наполовину прав. Но более реалистично вам это нужно, когда вы загружаете сборки во время выполнения, что означает, что тип не известен во время компиляции. C# будет сильно ограниченным, если вы не сможете этого сделать. Я имею в виду, что вы только что доказали это: как еще метод Activator, который выполняет работу экземпляра типа? Когда MS написала класс Activator, у них не было времени компиляции любых будущих типов, которые пользователи писали бы. 31 май. 172017-05-31 17:27:20

  0

@AnorZaken В моем комментарии ничего не говорится о создании типов во время выполнения. Конечно, вы можете это сделать, но вы не можете использовать их * статически * в том же контексте (конечно, вы можете разместить полную статически скомпилированную программу). Это все мой комментарий. 31 май. 172017-05-31 17:31:29

  0

@ KonradRudolph Ах, извините, неверно истолковал «такую ​​операцию», чтобы означать создание экземпляра типа, который известен только во время выполнения; вместо значения, использующего тип времени выполнения как параметр общего типа. 31 май. 172017-05-31 18:01:17


7

бы не общий T t = new T(); работы?

+7

Собственно, это было бы в универсальном классе/методе, но не для заданного «Тип». 19 сен. 102010-09-19 04:40:29


3
public AbstractType New 
{ 
    get 
    { 
     return (AbstractType) Activator.CreateInstance(GetType()); 
    } 
} 

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

9

Его довольно просто. Предположим, что ваше имя класса - Car, а пространство имен - Vehicles, затем передайте параметр как Vehicles.Car, который возвращает объект типа Car. Подобным образом вы можете динамически создавать любой экземпляр любого класса.

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

Если ваш Fully Qualified Name (т.е. Vehicles.Car в данном случае) в другой сборке, то Type.GetType будет нулевым. В таких случаях у вас есть петля через все сборки и найдите 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"); 
  0

В вашем втором случае (внешняя сборка) вы можете просто передать «Vehicles.Car, OtherAssembly» вашему первому методу, и он будет работать. Очевидно, что OtherAssembly - это имя сборки, в которой он живет. 20 окт. 162016-10-20 21:24:01

+1

@danmiser Для этого требуется жесткое кодирование имени сборки. Чтобы реализовать гибкость, я проверяю значение null, и код работает динамически: 21 окт. 162016-10-21 05:50:59


3

я могу по этому вопрос, потому что я искал, чтобы реализовать простой метод CloneObject для любого класса (с конструктором по умолчанию)

С универсальным методом вы можете потребовать, чтобы тип реализует новый().

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 

73

Скомпилированное выражение - лучший способ! (для производительности многократно создавать экземпляр во время выполнения).

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, 64):

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, x 86):

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) {} 
} 
+9

+1 для всех статистических данных! На данный момент мне не нужна такая производительность, но все же очень интересная. :) 01 фев. 162016-02-01 17:47:24

  0

Также есть TypeDescriptor.CreateInstance (см. Http://stackoverflow.com/a/17797389/1242), который может быть более быстрым, если используется с TypeDescriptor.AddProvider 20 апр. 162016-04-20 14:26:48

+1

Может ли это быть полезным, когда вы не знаете, какой тип 'X'? во время выполнения? 24 янв. 172017-01-24 14:41:10

  0

@ajeh Да. Измените тип (T) на Type.GetType (..). 24 янв. 172017-01-24 17:00:14

  0

Возможно, пример? 24 янв. 172017-01-24 17:19:39

+1

@ Serj-Tm Нет, это не сработает, если тип X является временем выполнения 'Тип'. 20 июл. 172017-07-20 00:52:38


8

без использования Reflection:

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

Как это полезно? Вы должны знать тип, уже вызывающий этот метод, и если вы знаете тип, который вы можете его построить без специального метода. 13 фев. 172017-02-13 02:21:49

  0

Так что T может меняться во время выполнения. Полезно, если вы работаете с deríved Types. 13 фев. 172017-02-13 09:41:45

  0

новый T(); будет терпеть неудачу, если T не является ссылочным типом с конструктором без параметров. Эти методы используют противопоказания, чтобы гарантировать, что T является ссылочным типом и имеет конструктор. 13 фев. 172017-02-13 09:46:41

+1

Как T меняется во время выполнения? Вам не нужно знать T во время разработки, чтобы вызвать Create <>? 15 фев. 172017-02-15 21:00:24

  0

Если вы работаете с универсальными классами и интерфейсами на фабриках, типы, реализующие интерфейс, должны быть инстанцированы, могут отличаться. 16 фев. 172017-02-16 07:48:43

  0

Вы знаете все возможные типы во время компиляции, но вы не знаете, какая реализация будет использоваться во время выполнения. 16 фев. 172017-02-16 07:54:31

  0

@RobertP. Во время выполнения вы можете создавать новые типы. Нет правила, согласно которому вы знаете все типы во время компиляции. И я не имею в виду с дженериками. Вы можете создавать совершенно новые типы и добавлять все поля, свойства и методы и т. Д. Во время выполнения. Существует также простой случай, когда вы хотите создать экземпляр типа, который находится в сборке, которая неизвестна во время компиляции. Это довольно часто. 31 май. 172017-05-31 00:47:13


5

Учитывая эту проблему Активатор будет работать, когда есть без параметров т е р. Если это ограничение, рассмотрите возможность использования

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()