2008-08-03 21 views

Respuesta

685

El Activator clase dentro del root System namespace es bastante poderoso.

Hay muchas sobrecargas para pasar parámetros al constructor y tal. Echa un vistazo a la documentación en:

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

o (nueva ruta)

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

Éstos son algunos ejemplos sencillos:

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

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

Me alegro de haber encontrado esto finalmente, pero la segunda llamada no es exactamente correcta, falta una cita y los parms invertidos deben ser: ObjectType instancia = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType"); – kevinc

+8

Debe llamar a 'Unwrap()' para obtener el tipo real de objeto que desea: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap(); –

34

Una aplicación de este problema es intentar llamar al constructor sin parámetros del tipo:

public static object GetNewObject(Type t) 
{ 
    try 
    { 
     return t.GetConstructor(new Type[] { }).Invoke(new object[] { }); 
    } 
    catch 
    { 
     return null; 
    } 
} 

Aquí es el mismo enfoque, contenido en un método genérico:

public static T GetNewObject<T>() 
{ 
    try 
    { 
     return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { }); 
    } 
    catch 
    { 
     return default(T); 
    } 
} 
+15

¿Programación por excepción? Esto parece una implementación muy pobre cuando simplemente puede reflexionar sobre el tipo para determinar los constructores. – Firoso

11

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 fáciles para la compilación dinámica Linq Expressions o algunos simples IL opcodes and DynamicMethod. De cualquier manera, la diferencia es enorme cuando comienzas a entrar en bucles estrechos o llamadas múltiples.

+0

El enlace "IL opcodes and DynamicMethod" está muerto. –

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

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

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

Excepto que esto no funciona para el tiempo de ejecución 'Type t'. –

+3

@Kevin Por supuesto. Tal operación * no puede * funcionar en un lenguaje estáticamente tipado porque no tiene sentido. No puede invocar métodos en un objeto de tipo desconocido.Mientras tanto (= desde que escribimos esta respuesta) C# tiene el constructo 'dynamic' que * does * permite dichos constructos, pero para la mayoría de los propósitos, esta respuesta aún lo cubre. –

+0

@KonradRudolph No es del todo cierto. Primero de C# * does * le permite crear nuevos tipos en tiempo de ejecución. No se puede llamar nada * de forma estáticamente segura *. Así que sí, eres medio correcto. Pero de forma más realista, necesita esto cuando carga ensamblajes en tiempo de ejecución, lo que significa que no se conoce el tipo en tiempo de compilación. C# estaría severamente limitado si no pudieras hacer esto. Quiero decir que simplemente lo has probado tú mismo: ¿de qué otro modo 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 ningún tipo futuro que los usuarios escribirían. – AnorZaken

7

¿No lo genérico T t = new T(); trabajo?

+7

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

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

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

Ejemplo utilizando la 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 } 
    ); 
9

Es bastante sencillo. 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 el objeto de tipo Car. De esta manera, puedes crear cualquier instancia de cualquier clase dinámicamente.

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

Si su Fully Qualified Name (es decir, Vehicles.Car en este caso) es en otro montaje, el Type.GetType será nulo. En tales casos, tiene un ciclo en todos los ensamblajes y encuentra 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"); 
+0

En su segundo caso (ensamble externo), podría pasar "Vehículos.Car, OtrosAsamblea" a su primer método y funcionará. Obviamente, OtherAssembly es el nombre del ensamblaje en el que vive. – danmiser

+1

@danmiser Necesita codificación dura del nombre del ensamblado. Para implementar flexibilidad, estoy verificando nulo y el código funciona de manera dinámica :) –

3

puedo través de esta pregunta porque yo estaba buscando para poner en práctica un método simple para CloneObject clase arbitraria (con un constructor por defecto)

Con método genérico se puede exigir que el tipo implementa Nueva().

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 asumir el tipo tiene un constructor por defecto y coger una excepción si no lo hace.

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

¡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, 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 

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 

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

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

+0

También está TypeDescriptor.CreateInstance (ver http://stackoverflow.com/a/17797389/1242), que puede ser más rápido si se usa con TypeDescriptor.AddProvider –

+1

¿Esto todavía es útil cuando no sabes qué tipo 'X' está en tiempo de ejecución? – ajeh

8

Sin el uso de Reflexión:

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

¿Cómo es útil? Ya debe saber el tipo para llamar a ese método y, si conoce el tipo, puede construirlo sin un método especial. –

+0

Entonces T puede variar en tiempo de ejecución. Útil si trabajas con tipos derivados. –

+0

una nueva T(); fallaría si T no es un tipo de referencia con un constructor sin parámetros. Este método usa contraints para asegurar que T sea un tipo de referencia y que tenga un constructor. –

5

Ante este problema, el activador funciona cuando hay un ctor sin parámetros. Si esto es una restricción, considere usar

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject() 
Cuestiones relacionadas