Как создать новый экземпляр объекта из Типа

Не всегда можно знать Type объекта во время компиляции, но может потребоваться создать экземпляр Type.

Как получить новый экземпляр объекта от Type?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
802
0
572 228
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Ответ принят как подходящий

Класс Activator в корневом пространстве имен System довольно мощный.

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

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

Рад, что наконец нашел это, но второй вызов не совсем правильный, отсутствует цитата, а пармы поменяны местами, должно быть: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespa‌ ce.ObjectType");

kevinc 03.06.2013 14:56

Вам нужно вызвать Unwrap (), чтобы получить фактический тип объекта, который вы хотите: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();

Ε Г И І И О 13.03.2014 19:08

Как ObjectType instance соответствует условию OP «Не всегда можно знать тип объекта во время компиляции»? :П

Martin Schneider 13.07.2018 12:13

@ МА-Маддин, ладно, object instance = Activator.CreateInstance(...);.

BrainSlugs83 16.10.2018 20:14

Кто-нибудь знает, как это сделать в .NET Core? Метод Unwrap недоступен для объекта.

Justin 18.01.2019 22:16

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

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

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

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

@Kevin Конечно. Такая операция не могу работает на статически типизированном языке, потому что она не имеет смысла. Вы не можете вызывать методы для объекта неизвестного типа. Между тем (= с момента написания этого ответа) C# имеет конструкцию dynamic, которая делает допускает такие конструкции, но для большинства целей этот ответ все еще покрывает ее.

Konrad Rudolph 07.04.2012 19:47

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

AnorZaken 31.05.2017 20:27

@AnorZaken В моем комментарии ничего не говорится о создании типов во время выполнения. Конечно, вы можете это сделать, но вы не можете использовать их статически в одном контексте (конечно, вы можете разместить полную статически скомпилированную программу). Это все, о чем говорится в моем комментарии.

Konrad Rudolph 31.05.2017 20:31

@KonradRudolph Эх, извините, неверно истолковано «такая операция» как означающая создание экземпляра типа, который известен только во время выполнения; вместо использования типа среды выполнения в качестве параметра универсального типа.

AnorZaken 31.05.2017 21:01

@AnorZaken - технически вы можете как создавать новые типы во время выполнения, так и вызывать для них методы статически безопасным способом. если: ваш новый тип реализует известный интерфейс или наследует известный базовый класс. - Любой из этих подходов даст вам статический контракт для вашего объекта, созданного во время выполнения.

BrainSlugs83 16.10.2018 20:17

Разве обычный T t = new T(); не подойдет?

Фактически, это было бы в общем классе / методе, но не для данного «Типа».

Brady Moritz 19.09.2010 08:40

Предполагает, что тип T имеет ограничение new ().

Rob Von Nesselrode 12.09.2018 06:02

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

Если вы хотите использовать конструктор по умолчанию, то решение с использованием 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[] был именно тем, что я хотел найти, чтобы узнать, спасибо!

Chad 06.09.2019 09:17

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

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

Если ваш Полное имя (то есть 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");

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

danmiser 21.10.2016 00:24

@danmiser Это требует жесткого кодирования имени сборки. Для обеспечения гибкости я проверяю null, и код работает динамически :)

Sarath KS 21.10.2016 08:50

Я могу ответить на этот вопрос, потому что я хотел реализовать простой метод 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

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

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

+1 за всю статистику! На данный момент мне не особо нужен такой спектакль, но он все равно очень интересен. :)

AnorZaken 01.02.2016 20:47

Также есть TypeDescriptor.CreateInstance (см. stackoverflow.com/a/17797389/1242), который может быть быстрее при использовании с TypeDescriptor.AddProvider

Lars Truijens 20.04.2016 17:26

Это все еще полезно, если вы не знаете, какой тип X находится во время выполнения?

ajeh 24.01.2017 17:41

@ajeh Да. Измените typeof (T) на Type.GetType (..).

Serj-Tm 24.01.2017 20:00

@ Serj-Tm Нет, это не сработает, если тип X - это среда выполнения Type.

NetMage 20.07.2017 03:52

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

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

Чем это полезно? Вы должны знать тип, чтобы вызвать этот метод, и если вы знаете тип, вы можете создать его без специального метода.

Kyle Delaney 13.02.2017 05:21

Таким образом, T может меняться во время выполнения. Полезно, если вы работаете с производными типами.

user887983 13.02.2017 12:41

новый T (); завершится ошибкой, если T не является ссылочным типом с конструктором без параметров. Эти методы используют ограничения, чтобы гарантировать, что T является ссылочным типом и имеет конструктор.

user887983 13.02.2017 12:46

Как T может меняться во время выполнения? Разве вам не нужно знать T во время разработки, чтобы вызвать Create?

Kyle Delaney 16.02.2017 00:00

Если вы работаете с универсальными классами и интерфейсами на фабриках, типы, реализующие интерфейс, которые должны быть созданы, могут различаться.

user887983 16.02.2017 10:48

Вы знаете все возможные типы во время компиляции, но не знаете, какая реализация будет использоваться во время выполнения.

user887983 16.02.2017 10:54

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

AnorZaken 31.05.2017 03:47

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

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()

Так же, как дополнительный для всех, кто использует приведенные выше ответы, которые реализуют:

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

Будьте осторожны - если ваш конструктор не является общедоступным, вы получите следующую ошибку:

"System.MissingMethodException: 'No parameterless constructor defined for this object."

Ваш класс может быть Internal / Friend или любым другим, но конструктор должен быть общедоступным.

Другие вопросы по теме