Cómo crear una nueva instancia de objeto a partir de un tipo

Uno no siempre puede saber el Type de un objeto en tiempo de compilación, pero es posible que deba crear una instancia del Type.

¿Cómo se obtiene una nueva instancia de objeto de un Type?

preguntado el 03 de agosto de 08 a las 14:08

12 Respuestas

La Activator clase dentro de la raíz System el espacio de nombres es bastante poderoso.

Hay muchas sobrecargas para pasar parámetros al constructor y demás. Consulte la documentación en:

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

o (nuevo camino)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

A continuación, se muestran algunos ejemplos sencillos:

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

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");

respondido 08 nov., 17:15

Me alegro de haber encontrado finalmente esto, pero la segunda llamada no es exactamente correcta, falta una cita y los parámetros están invertidos, debería ser: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType"); - kevinc

Debe llamar a 'Unwrap ()' para obtener el tipo real de objeto que desea: instancia de ConcreteType = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap (); - Ε Ã И І И О

¿Cómo ObjectType instance coincidir con la condición del OP "Uno no siempre puede saber el tipo de un objeto en tiempo de compilación"? :PAG - Martín Schneider

@ MA-Maddin está bien entonces, object instance = Activator.CreateInstance(...);. - BrainSlugs83

¿Alguien sabe cómo hacer esto en .NET Core? El método Unwrap no está disponible en el objeto. - Justin

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

La Activator La clase tiene una variante genérica que hace que esto sea un poco más fácil:

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

contestado el 16 de mayo de 16 a las 13:05

@Kevin Por supuesto. Tal operación no se puede trabajar en un lenguaje escrito estáticamente porque no tiene sentido. No puede invocar métodos en un objeto de tipo desconocido. Mientras tanto (= desde que escribí esta respuesta) C # tiene el dynamic construir cual permiten tales construcciones, pero para la mayoría de los propósitos esta respuesta aún lo cubre. - Konrad Rodolfo

@KonradRudolph No es del todo cierto. Primero de c # le permite crear nuevos tipos en tiempo de ejecución. Simplemente no puedes llamarles nada de una manera estáticamente segura. Así que sí, estás medio en lo cierto. Pero de manera más realista, necesita esto cuando carga ensamblados en tiempo de ejecución, lo que significa que el tipo no se conoce en tiempo de compilación. C # estaría muy limitado si no pudiera hacer esto. Quiero decir que lo acaba de demostrar usted mismo: ¿de qué otra manera funciona el método Activator que toma una instancia de tipo? Cuando MS escribió la clase Activator, no tenían conocimiento en tiempo de compilación de los tipos futuros que escribirían los usuarios. - anorzaken

@AnorZaken Mi comentario no dice nada sobre la creación de tipos en tiempo de ejecución. Por supuesto que puedes hacer eso, pero no puedes usarlos. inactivamente en el mismo contexto (por supuesto, puede alojar un programa completo compilado estáticamente). Eso es todo lo que dice mi comentario. - Konrad Rodolfo

@KonradRudolph Ah, lo siento, malinterpreté "una operación de este tipo" en el sentido de crear una instancia de un tipo que solo se conoce en tiempo de ejecución; en lugar de significar usar un tipo de tiempo de ejecución como parámetro de tipo genérico. - anorzaken

@AnorZaken: técnicamente, puede crear nuevos tipos en tiempo de ejecución Y llamar a métodos en ellos de una manera estáticamente segura if su nuevo tipo implementa una interfaz conocida o hereda una clase base conocida. - Cualquiera de esos enfoques le dará un contrato estático para su objeto creado en tiempo de ejecución. - BrainSlugs83

¡La expresión compilada es la mejor manera! (para que el rendimiento cree una instancia repetidamente en tiempo de ejecución).

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();

X x = YCreator();

Estadísticas (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

Estadísticas (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

Estadísticas (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

Estadísticas (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

Estadísticas (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

Estadísticas (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

Código completo:

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

contestado el 12 de mayo de 19 a las 12:05

+1 para todas las estadísticas! Realmente no necesito este tipo de actuación en este momento, pero sigue siendo muy interesante. :) - anorzaken

También existe TypeDescriptor.CreateInstance (ver stackoverflow.com/a/17797389/1242) que puede ser más rápido si se usa con TypeDescriptor.AddProvider - lars truijens

¿Sigue siendo útil cuando no sabe de qué tipo? X está en tiempo de ejecución? - ajeh

@ajeh Sí. Cambie typeof (T) a Type.GetType (..). - Serj-Tm

@ Serj-Tm No, eso no funcionará si el tipo X es un tiempo de ejecución Type. - Mago de red

Es bastante simple. Suponga que su nombre de clase es Car y el espacio de nombres es Vehicles, luego pase el parámetro como Vehicles.Car que devuelve objeto de tipo Car. De esta manera, puede crear cualquier instancia de cualquier clase de forma dinámica.

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

Si Nombre completamente calificado(Es decir, Vehicles.Car en este caso) está en otra asamblea, el Type.GetType será nulo. En tales casos, tiene que recorrer todos los ensamblajes y encontrar el Type. Para eso, puede usar el siguiente código

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

Y puede obtener la instancia llamando al método anterior.

object objClassInstance = GetInstance("Vehicles.Car");

Respondido 09 Abr '15, 16:04

En su segundo caso (ensamblaje externo), simplemente podría pasar "Vehicles.Car, OtherAssembly" a su primer método y funcionará. Obviamente, OtherAssembly es el nombre de la asamblea en la que vive. danmisero

@danmiser Eso necesita codificar el nombre del ensamblado. Para implementar la flexibilidad, estoy verificando nulo y el código funciona de manera dinámica :) - Sarath KS

Si esto es para algo que se llamará mucho en una instancia de aplicación, es mucho más rápido compilar y almacenar en caché el código dinámico en lugar de usar el activador o ConstructorInfo.Invoke(). Se compilan dos opciones sencillas para la compilación dinámica Expresiones Linq o algo simple IL códigos de operación y DynamicMethod. De cualquier manera, la diferencia es enorme cuando comienza a entrar en bucles estrechos o múltiples llamadas.

Respondido el 14 de junio de 17 a las 07:06

Sin uso de Reflection:

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

Respondido 23 Abr '20, 19:04

¿Cómo es esto útil? Debe conocer el tipo ya para llamar a ese método, y si conoce el tipo, puede construirlo sin un método especial. - kyle delaney

Entonces T puede variar en tiempo de ejecución. Útil si trabaja con tipos derivados. - usuario887983

una nueva T (); fallaría si T no es un tipo de referencia con un constructor sin parámetros. Este método usa restricciones para garantizar que T es un tipo de referencia y tiene un constructor. - usuario887983

¿Cómo puede variar T en tiempo de ejecución? ¿No tiene que conocer T en tiempo de diseño para llamar a Create <>? - kyle delaney

Si trabaja con clases e interfaces genéricas en fábricas, los tipos que implementan la interfaz deben instanciarse pueden variar. - usuario887983

Si desea usar el constructor predeterminado, la solución que usa System.Activator presentado anteriormente es probablemente el más conveniente. Sin embargo, si el tipo carece de un constructor predeterminado o tiene que usar uno que no sea el predeterminado, entonces una opción es usar la reflexión o System.ComponentModel.TypeDescriptor. En caso de reflexión, basta con saber solo el nombre del tipo (con su espacio de nombres).

Ejemplo usando reflexión:

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

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

Respondido 22 Jul 13, 22:07

args[] fue exactamente lo que vine a buscar a esta pregunta, ¡gracias! - Chad

¿No sería el genérico T t = new T(); ¿trabajo?

respondido 30 nov., 15:15

En realidad, lo haría en una clase / método genérico, pero no para un "Tipo" dado. - brady moritz

Asume que el tipo T tiene la restricción 'new ()'. - Rob Von Nesselrode

Dado este problema, el Activator funcionará cuando haya un ctor sin parámetros. Si esto es una restricción, considere usar

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()

Respondido 02 Jul 15, 07:07

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

contestado el 16 de mayo de 16 a las 14:05

Puedo responder a esta pregunta porque estaba buscando implementar un método CloneObject simple para una clase arbitraria (con un constructor predeterminado)

Con el método genérico puede requerir que el tipo implemente 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

Con no genérico, suponga que el tipo tiene un constructor predeterminado y captura una excepción si no lo tiene.

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

respondido 24 mar '15, 18:03

Solo como un extra para cualquiera que use las respuestas anteriores que implementan:

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

Tenga cuidado: si su Constructor no es "Público", obtendrá el siguiente error:

"System.MissingMethodException: 'No se ha definido ningún constructor sin parámetros para este objeto".

Tu clase puede ser Interna / Amiga, o lo que necesites, pero el constructor debe ser público.

Respondido 13 ago 20, 12:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.