Propósito de Activator.CreateInstance con ejemplo?

124

¿Alguien puede explicar el Activator.CreateInstance()propósito en detalle?

Tabriz Atayi
fuente
66
Probablemente, la mayor documentación y ejemplo no está disponible en las sobrecargas genéricas. Animaría a que la documentación de CreateInstance(Type type)coincida con la CreateInstance<T>()sobrecarga.
Claus Jørgensen
44
La página de MSDN solo tiene una sola línea explicativa, "Contiene métodos para crear tipos de objetos local o remotamente, u obtener referencias a objetos remotos existentes. Esta clase no se puede heredar". msdn.microsoft.com/en-us/library/system.activator.aspx
gonzobrains
2
Si has venido aquí desde un fondo Java, esta es la c#.netforma de hacerlo Object xyz = Class.forName(className).newInstance();.
SNag
Hay una mejor documentación para esto aquí .
jpaugh

Respuestas:

149

Digamos que tiene una clase llamada MyFancyObjectcomo esta a continuación:

class MyFancyObject
{
 public int A { get;set;}
}

Te permite convertir:

String ClassName = "MyFancyObject";

Dentro

MyFancyObject obj;

Utilizando

obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))

y luego puede hacer cosas como:

obj.A = 100;

Ese es su propósito. También tiene muchas otras sobrecargas, como proporcionar un Typenombre en lugar del nombre de la clase en una cadena. Por qué tendría un problema así es una historia diferente. Aquí hay algunas personas que lo necesitaban:

deepee1
fuente
2
Esto me resultó útil. En mi caso, la clase estaba en un espacio de nombres diferente, así que tuve que asegurarme de incluir el espacio de nombres en mi cadena de ClassName (es decir String ClassName = "My.Namespace.MyFancyObject";).
Scott
1
Olvidaste agregar el Unwrap (). También puede poner nulo, en lugar de "MyAssembly", y el sistema buscará el ensamblado actual.
Tony
1
¿Puedo hacer algo como esto obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))pero en lugar de lanzar con el tipo? ¿Reparto con tipo hecho desde ClassName? Me gusta esto Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))?
Rluks
1
¿En qué se diferencia lo anterior de: MyFancyObject obj = new MyFancyObject?
Ed Landau
1
@Ed Landau La diferencia es que puede crear instancias de un objeto en tiempo de ejecución que no sabe que es tipo en tiempo de compilación. Por ejemplo, si estaba creando un sistema de complementos para su programa. Los enlaces en la respuesta podrían darle algunas ideas sobre cuándo no sería posible escribir MyFancyObject obj = new MyFancyObject (). Esto a menudo se combinaría con el uso de la reflexión en general. También puede consultar stackoverflow.com/questions/9409293/… para obtener otra descripción.
deepee1
47

Bueno, puedo darte un ejemplo de por qué usar algo así. Piensa en un juego donde quieras almacenar tu nivel y tus enemigos en un archivo XML. Cuando analiza este archivo, es posible que tenga un elemento como este.

<Enemy X="10" Y="100" Type="MyGame.OrcGuard"/>

lo que puede hacer ahora es crear dinámicamente los objetos encontrados en su archivo de nivel.

foreach(XmlNode node in doc)
   var enemy = Activator.CreateInstance(null, node.Attributes["Type"]);

Esto es muy útil para construir entornos dinámicos. Por supuesto, también es posible usar esto para escenarios de complementos o complementos y mucho más.

Dowhilefor
fuente
55
Comprendí que era crear una nueva instancia de un tipo, pero este es un buen ejemplo simple de por qué uno querría hacerlo.
jamiebarrow
14

Mi buen amigo MSDN te lo puede explicar, con un ejemplo

Aquí está el código en caso de que el enlace o el contenido cambien en el futuro:

using System;

class DynamicInstanceList
{
    private static string instanceSpec = "System.EventArgs;System.Random;" +
        "System.Exception;System.Object;System.Version";

    public static void Main()
    {
        string[] instances = instanceSpec.Split(';');
        Array instlist = Array.CreateInstance(typeof(object), instances.Length);
        object item;
        for (int i = 0; i < instances.Length; i++)
        {
            // create the object from the specification string
            Console.WriteLine("Creating instance of: {0}", instances[i]);
            item = Activator.CreateInstance(Type.GetType(instances[i]));
            instlist.SetValue(item, i);
        }
        Console.WriteLine("\nObjects and their default values:\n");
        foreach (object o in instlist)
        {
            Console.WriteLine("Type:     {0}\nValue:    {1}\nHashCode: {2}\n",
                o.GetType().FullName, o.ToString(), o.GetHashCode());
        }
    }
}

// This program will display output similar to the following: 
// 
// Creating instance of: System.EventArgs 
// Creating instance of: System.Random 
// Creating instance of: System.Exception 
// Creating instance of: System.Object 
// Creating instance of: System.Version 
// 
// Objects and their default values: 
// 
// Type:     System.EventArgs 
// Value:    System.EventArgs 
// HashCode: 46104728 
// 
// Type:     System.Random 
// Value:    System.Random 
// HashCode: 12289376 
// 
// Type:     System.Exception 
// Value:    System.Exception: Exception of type 'System.Exception' was thrown. 
// HashCode: 55530882 
// 
// Type:     System.Object 
// Value:    System.Object 
// HashCode: 30015890 
// 
// Type:     System.Version 
// Value:    0.0 
// HashCode: 1048575
Claus Jørgensen
fuente
1
Es poco probable que ocurra para MSDN, y copiar el contenido aquí es casi una violación de los derechos de autor;)
Claus Jørgensen
13
Tienes razón. Personalmente, creo que el principio también funciona para dar mejores respuestas. A menudo vengo a SO con muchas cosas en mente de mi proyecto actual. Por lo general, solo quiero una respuesta simple y directa, para poder continuar donde lo dejé. Odio tener que abrir artículos, que a veces incluso enlazan con otros artículos. Muchos respondedores no se dan cuenta de que muchas personas no vienen aquí con tiempo libre para leer varios artículos, con un montón de presentaciones y charlas innecesarias. Resumir brevemente las partes importantes de un buen artículo es clave para algunas de las mejores respuestas que he visto.
Aske B.
10

También puedes hacer esto:

var handle = Activator.CreateInstance("AssemblyName", 
                "Full name of the class including the namespace and class name");
var obj = handle.Unwrap();
Guillermo
fuente
Unwrap () era la pieza que faltaba. :)
Tony
No puedo encontrar el método 'Desenvolver ()' anterior para usar (como arriba). ¿Ha cambiado algo en las nuevas API de .NET?
Sam
Todavía está allí en el espacio de nombres del sistema.
William
2
¿Podría proporcionar alguna explicación adicional sobre lo que .Unwrap()hace precisamente y cómo se relaciona esto con otras soluciones?
Jeroen Vannevel
@Sam está en una sobrecarga diferente de CreateInstancedonde regresa System.Runtime.Remoting.ObjectHandle.
nawfal
9

Un buen ejemplo podría ser el siguiente: por ejemplo, tiene un conjunto de registradores y permite al usuario especificar el tipo que se utilizará en tiempo de ejecución a través del archivo de configuración.

Luego:

string rawLoggerType = configurationService.GetLoggerType();
Type loggerType = Type.GetType(rawLoggerType);
ILogger logger = Activator.CreateInstance(loggerType.GetType()) as ILogger;

O otro caso es cuando tiene una fábrica de entidades comunes, que crea una entidad, y también es responsable de la inicialización de una entidad por los datos recibidos de DB:

(pseudocódigo)

public TEntity CreateEntityFromDataRow<TEntity>(DataRow row)
 where TEntity : IDbEntity, class
{
   MethodInfo methodInfo = typeof(T).GetMethod("BuildFromDataRow");
   TEntity instance = Activator.CreateInstance(typeof(TEntity)) as TEntity;
   return methodInfo.Invoke(instance, new object[] { row } ) as TEntity;
}
sll
fuente
Esto no funciona, typeof(loggerType)resultados enloggerType is a variable and used like a type
Barkermn01
3

El Activator.CreateInstancemétodo crea una instancia de un tipo especificado utilizando el constructor que mejor coincide con los parámetros especificados.

Por ejemplo, supongamos que tiene el nombre del tipo como una cadena y desea usar la cadena para crear una instancia de ese tipo. Podrías usar Activator.CreateInstancepara esto:

string objTypeName = "Foo";
Foo foo = (Foo)Activator.CreateInstance(Type.GetType(objTypeName));

Aquí hay un artículo de MSDN que explica su aplicación con más detalle:

http://msdn.microsoft.com/en-us/library/wccyzw83.aspx

James Johnson
fuente
55
O simplemente podrías usarlo new Foo(). Creo que el OP quería un ejemplo más realista.
Konrad Rudolph
1
Estoy de acuerdo con @Konrad. La razón para usar CreateInstancees si no conoce el tipo de objeto que va a instanciar en el momento del diseño. En este ejemplo, claramente sabe que es de tipo, Fooya que lo está convirtiendo como tipo Foo. Nunca harías esto porque solo puedes hacerlo Foo foo = new Foo().
theyetiman
1

Partiendo de deepee1 y esto , aquí se explica cómo aceptar un nombre de clase en una cadena y luego usarlo para leer y escribir en una base de datos con LINQ. Uso "dinámico" en lugar de la conversión de deepee1 porque me permite asignar propiedades, lo que nos permite seleccionar dinámicamente y operar en cualquier tabla que queramos.

Type tableType = Assembly.GetExecutingAssembly().GetType("NameSpace.TableName");
ITable itable = dbcontext.GetTable(tableType);

//prints contents of the table
foreach (object y in itable) {
    string value = (string)y.GetType().GetProperty("ColumnName").GetValue(y, null);
    Console.WriteLine(value);
}

//inserting into a table
dynamic tableClass = Activator.CreateInstance(tableType);
//Alternative to using tableType, using Tony's tips
dynamic tableClass = Activator.CreateInstance(null, "NameSpace.TableName").Unwrap();
tableClass.Word = userParameter;
itable.InsertOnSubmit(tableClass);
dbcontext.SubmitChanges();

//sql equivalent
dbcontext.ExecuteCommand("INSERT INTO [TableNme]([ColumnName]) VALUES ({0})", userParameter);
DharmaTurtle
fuente
0

¿Por qué lo usarías si ya conocías la clase y la ibas a lanzar? ¿Por qué no simplemente hacerlo a la antigua usanza y hacer que la clase como siempre lo hagas? Esto no tiene ninguna ventaja sobre la forma en que se hace normalmente. ¿Hay alguna manera de tomar el texto y operarlo de esta manera?

label1.txt = "Pizza" 
Magic(label1.txt) p = new Magic(lablel1.txt)(arg1, arg2, arg3);
p.method1();
p.method2();

Si ya sé que es una pizza, no hay ventaja para:

p = (Pizza)somefancyjunk("Pizza"); over
Pizza p = new Pizza();

pero veo una gran ventaja para el método Magic si existe.

usuario8659016
fuente
0

Junto con la reflexión, descubrí que Activator.CreateInstance es muy útil para asignar el resultado del procedimiento almacenado a una clase personalizada como se describe en la siguiente respuesta .

útilBee
fuente