Scopo di Activator.CreateInstance con esempio?


124

Qualcuno può spiegare lo Activator.CreateInstance()scopo in dettaglio?


6
Probabilmente la documentazione e l'esempio maggiori non sono disponibili negli overload generici. Vorrei incoraggiare che la documentazione di CreateInstance(Type type)è abbinata al CreateInstance<T>()sovraccarico.
Claus Jørgensen

4
La pagina MSDN ha solo una singola riga esplicativa, "Contiene metodi per creare tipi di oggetti localmente o in remoto o per ottenere riferimenti a oggetti remoti esistenti. Questa classe non può essere ereditata". msdn.microsoft.com/en-us/library/system.activator.aspx
gonzobrains

2
Se sei arrivato qui da un background Java, questo è il c#.netmodo di fare Object xyz = Class.forName(className).newInstance();.
Snag

C'è una migliore documentazione per questo qui .
jpaugh

Risposte:


149

Supponi di avere una classe chiamata MyFancyObjectcome questa di seguito:

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

Ti consente di trasformare:

String ClassName = "MyFancyObject";

In

MyFancyObject obj;

utilizzando

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

e può quindi fare cose come:

obj.A = 100;

Questo è il suo scopo. Ha anche molti altri overload come fornire un Typeinvece del nome della classe in una stringa. Perché avresti un problema del genere è una storia diversa. Ecco alcune persone che ne avevano bisogno:


2
Questo mi è stato utile. Nel mio caso, la classe era in uno spazio dei nomi diverso, quindi dovevo assicurarmi di includere lo spazio dei nomi nella mia stringa ClassName (cioè String ClassName = "My.Namespace.MyFancyObject";).
Scott

1
Hai dimenticato di aggiungere Unwrap (). È anche possibile inserire null, invece di "MyAssembly", e il sistema cercherà l'assembly corrente.
Tony

1
Posso fare qualcosa di simile obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))ma invece di lanciare con il tipo. Cast con il tipo creato da ClassName? Come questo Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))?
rluks

1
In che modo quanto sopra è diverso da: MyFancyObject obj = new MyFancyObject?
Ed Landau

1
@Ed Landau La differenza è che puoi istanziare un oggetto in fase di esecuzione di cui non conosci il tipo in fase di compilazione. Ad esempio, se stavi creando un sistema di plugin per il tuo programma. I collegamenti nella risposta potrebbero darti alcune idee su quando non sarebbe possibile scrivere semplicemente MyFancyObject obj = new MyFancyObject (). A volte questo sarebbe stato associato all'uso della riflessione in generale. Puoi anche controllare stackoverflow.com/questions/9409293/… per un'altra descrizione.
deepee1

47

Bene, posso darti un esempio del perché usare qualcosa del genere. Pensa a un gioco in cui vuoi memorizzare il tuo livello e i nemici in un file XML. Quando analizzi questo file, potresti avere un elemento come questo.

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

quello che puoi fare ora è creare dinamicamente gli oggetti trovati nel tuo file di livello.

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

Questo è molto utile per costruire ambienti dinamici. Ovviamente è anche possibile usarlo per plugin o scenari aggiuntivi e molto altro ancora.


5
Ho capito che si trattava di creare una nuova istanza di un tipo, ma questo è un bell'esempio semplice del motivo per cui si vorrebbe farlo.
jamiebarrow

14

Il mio buon amico MSDN può spiegartelo, con un esempio

Ecco il codice nel caso in cui il collegamento o il contenuto cambino in 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

1
È improbabile che accada per MSDN e la copia del contenuto qui è quasi una violazione del copyright;)
Claus Jørgensen

13
Hai ragione. Personalmente ritengo che il principio funzioni anche per dare risposte migliori. Vengo spesso a SO con molto in mente dal mio progetto attuale. Di solito desidero solo una risposta semplice e precisa, così posso continuare da dove avevo interrotto. Odio dover aprire articoli, che a volte rimandano anche ad altri articoli. Molti risponditori non si rendono conto che molte persone non vengono qui con il tempo a disposizione per leggere diversi articoli, con un sacco di presentazioni e discorsi inutili. Riassumere brevemente le parti importanti di un buon articolo è la chiave per alcune delle più grandi risposte che ho visto.
Aske B.

10

Puoi anche farlo -

var handle = Activator.CreateInstance("AssemblyName", 
                "Full name of the class including the namespace and class name");
var obj = handle.Unwrap();

Il Unwrap () era il pezzo mancante. :)
Tony

Non riesco a trovare il metodo "Unwrap ()" sopra da utilizzare (come sopra). Qualcosa è cambiato nelle nuove API .NET?
Sam

È ancora presente nello spazio dei nomi di sistema.
William

2
Potresti fornire qualche spiegazione aggiuntiva su cosa .Unwrap()fa precisamente e come questo si collega ad altre soluzioni?
Jeroen Vannevel

@ Sam è su un sovraccarico diverso da CreateInstancedove ritorna System.Runtime.Remoting.ObjectHandle.
nawfal

9

Un buon esempio potrebbe essere il prossimo: ad esempio si dispone di un set di logger e si consente all'utente di specificare il tipo da utilizzare in runtime tramite il file di configurazione.

Poi:

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

O un altro caso è quando si dispone di una fabbrica di entità comune, che crea entità ed è anche responsabile dell'inizializzazione di un'entità dai dati ricevuti dal DB:

(Pseudocodice)

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

Questo non funziona, si typeof(loggerType)traduce inloggerType is a variable and used like a type
Barkermn01

3

Il Activator.CreateInstance metodo crea un'istanza di un tipo specificato utilizzando il costruttore che meglio corrisponde ai parametri specificati.

Ad esempio, supponiamo di avere il nome del tipo come stringa e di voler utilizzare la stringa per creare un'istanza di quel tipo. Potresti usare Activator.CreateInstanceper questo:

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

Ecco un articolo di MSDN che spiega la sua applicazione in modo più dettagliato:

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


5
Oppure potresti semplicemente usare new Foo(). Penso che l'OP volesse un esempio più realistico.
Konrad Rudolph,

1
Sono d'accordo con @Konrad. Il motivo per l'utilizzo CreateInstanceè se non si conosce il tipo di oggetto che si intende creare in fase di progettazione. In questo esempio, sai chiaramente che è di tipo Foopoiché lo lanci come tipo Foo. Non lo faresti mai perché puoi farlo e basta Foo foo = new Foo().
theyetiman

1

Basandosi su deepee1 e questo , ecco come accettare un nome di classe in una stringa e quindi usarlo per leggere e scrivere in un database con LINQ. Uso "dynamic" invece del casting di deepee1 perché mi permette di assegnare proprietà, che ci permette di selezionare dinamicamente e operare su qualsiasi tabella desideriamo.

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

0

Perché dovresti usarlo se conoscessi già la classe e volessi lanciarlo? Perché non farlo semplicemente alla vecchia maniera e fare in modo che la classe ce la fai sempre? Non c'è alcun vantaggio in questo rispetto al modo in cui è fatto normalmente. C'è un modo per prendere il testo e operare su di esso in questo modo:

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

Se so già che è una pizza non c'è alcun vantaggio a:

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

ma vedo un enorme vantaggio nel metodo Magic, se esiste.


0

Insieme alla riflessione, ho trovato Activator.CreateInstance molto utile per mappare il risultato della stored procedure su una classe personalizzata come descritto nella risposta seguente .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.