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

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

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

3.08.2008 16:29:03
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");
872
8.11.2017 15:40:10
Рад, что, наконец, нашел это, но второй вызов не совсем правильно, пропуская кавычку и обратные пармы, должно быть: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType");
kevinc 3.06.2013 10:56:15
Вам нужно вызвать 'Unwrap ()', чтобы получить фактический тип объекта, который вы хотите: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
Ε Г И І И О 13.03.2014 15:08:47
Как ObjectType instanceсоответствует условию OP «Не всегда может быть известен тип объекта во время компиляции»? : P
Martin Schneider 13.07.2018 09:13:20
@ MA-Maddin тогда ладно object instance = Activator.CreateInstance(...);.
BrainSlugs83 16.10.2018 17:14:01
Кто-нибудь знает, как это сделать в .NET Core? Метод Unwrap недоступен для объекта.
Justin 18.01.2019 19:16:00
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

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

ObjectType instance = Activator.CreateInstance<ObjectType>();
144
16.05.2016 12:59:11
@Kevin Конечно. Такая операция не может работать на статически типизированном языке, потому что она не имеет смысла. Вы не можете вызывать методы для объекта неизвестного типа. В то же время (= так как пишу этот ответ) C # получила dynamicконструкцию , которая делает позволяют такие конструкции , но для большинства целей этот ответ еще охватывает ее.
Konrad Rudolph 7.04.2012 15:47:59
@KonradRudolph Не совсем так. Прежде C # это позволяет создавать новые типы во время выполнения. Вы просто не можете ничего назвать на них статически безопасным способом . Так что да, ты наполовину прав. Но более реалистично это необходимо при загрузке сборок во время выполнения, что означает, что тип не известен во время компиляции. C # будет строго ограничен, если вы не сможете сделать это. Я имею в виду, вы только что сами это доказали: как еще работает метод Activator, который принимает экземпляр типа? Когда MS написала класс Activator, они не знали во время компиляции о будущих типах, которые будут писать пользователи.
AnorZaken 31.05.2017 17:27:20
@AnorZaken Мой комментарий ничего не говорит о создании типов во время выполнения. Конечно, вы можете сделать это, но вы не можете использовать их статически в одном и том же контексте (конечно, вы можете разместить полную статически скомпилированную программу). Это все, что говорит мой комментарий.
Konrad Rudolph 31.05.2017 17:31:29
@KonradRudolph Извините, неверно истолковали «такую ​​операцию», чтобы обозначить создание экземпляра типа, который известен только во время выполнения; вместо смысла использовать тип времени выполнения в качестве параметра общего типа.
AnorZaken 31.05.2017 18:01:17
@AnorZaken - технически вы можете создавать новые типы во время выполнения и вызывать методы для них статически безопасным способом, если ваш новый тип реализует известный интерфейс или наследует известный базовый класс. - Любой из этих подходов даст вам статический контракт для вашего объекта, созданного во время выполнения.
BrainSlugs83 16.10.2018 17:17:08

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

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) {}
}
126
12.05.2019 11:08:22
+1 за всю статистику! На данный момент мне не нужно такое представление, но все равно очень интересно. :)
AnorZaken 1.02.2016 17:47:24
Также есть TypeDescriptor.CreateInstance (см. Stackoverflow.com/a/17797389/1242 ), который может быть быстрее, если используется с TypeDescriptor.AddProvider
Lars Truijens 20.04.2016 14:26:48
Это все еще полезно, когда вы не знаете, какой тип Xво время выполнения?
ajeh 24.01.2017 14:41:10
@ajeh Да. Измените typeof (T) на Type.GetType (..).
Serj-Tm 24.01.2017 17:00:14
@ Serj-Tm Нет, это не сработает, если тип X - это среда выполнения Type.
NetMage 20.07.2017 00:52:38

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

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);
    }
}
47
16.05.2016 12:58:54
Исключительное программирование? Это кажется очень плохой реализацией, когда вы можете просто размышлять над типом для определения конструкторов.
Firoso 22.03.2014 21:57:41

Это довольно просто. Предположим, что ваше имя класса 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");
16
9.04.2015 15:19:59
Во втором случае (внешняя сборка) вы можете просто передать «Vehicles.Car, OtherAssembly» своему первому методу, и он будет работать. Очевидно, что OtherAssembly - это название сборки, в которой он живет.
danmiser 20.10.2016 21:24:01
@danmiser Это требует жесткого кодирования имени сборки. Чтобы реализовать гибкость, я проверяю ноль, и код работает динамически :)
Sarath Avanavu 21.10.2016 05:50:59

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

14
14.06.2017 06:14:00

Не будет ли общая T t = new T();работа?

11
30.11.2015 15:30:51
На самом деле, это было бы в общем классе / методе, но не для данного «Типа».
Brady Moritz 19.09.2010 04:40:29
Предполагается, что тип T имеет ограничение 'new ()'.
Rob Von Nesselrode 12.09.2018 03:02:14

Если вы хотите использовать конструктор по умолчанию, то решение с использованием 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 }
    );
10
22.07.2013 21:03:41
args[]было именно то, что я пришел к этому вопросу, чтобы найти, спасибо!
Chad 6.09.2019 06:17:53

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

private T Create<T>() where T : class, new()
{
    return new T();
}
10
19.04.2020 17:57:38
Чем это полезно? Вы уже должны знать тип для вызова этого метода, и если вы знаете тип, вы можете создать его без специального метода.
Kyle Delaney 13.02.2017 02:21:49
Так что T может меняться во время выполнения. Полезно, если вы работаете с производными типами.
user887983 13.02.2017 09:41:45
новый T (); потерпит неудачу, если T не является ссылочным типом с конструктором без параметров. Этот метод использует ограничения, чтобы гарантировать, что T является ссылочным типом и имеет конструктор.
user887983 13.02.2017 09:46:41
Как T может меняться во время выполнения? Разве вы не должны знать T во время разработки, чтобы вызвать Create <>?
Kyle Delaney 15.02.2017 21:00:24
Если вы работаете с универсальными классами и интерфейсами на фабриках, типы, которые реализуют интерфейс, должны быть созданы, могут отличаться.
user887983 16.02.2017 07:48:43

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

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
7
2.07.2015 06:29:29
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}
5
16.05.2016 13:00:25

Я могу ответить на этот вопрос, потому что я искал реализовать простой метод 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
4
24.03.2015 18:10:05