But de Activator.CreateInstance avec exemple?

124

Quelqu'un peut-il expliquer le Activator.CreateInstance()but en détail?

Tabriz Atayi
la source
6
Probablement que la plus grande documentation et l'exemple ne sont pas disponibles dans les surcharges génériques. J'encouragerais que la documentation de CreateInstance(Type type)corresponde à la CreateInstance<T>()surcharge.
Claus Jørgensen
4
La page MSDN n'a qu'une seule ligne explicative, «Contient des méthodes pour créer des types d'objets localement ou à distance, ou pour obtenir des références à des objets distants existants. Cette classe ne peut pas être héritée». msdn.microsoft.com/en-us/library/system.activator.aspx
gonzobrains
2
Si vous venez ici d'un arrière-plan Java, c'est la c#.netfaçon de faire Object xyz = Class.forName(className).newInstance();.
SNag
Il y a une meilleure documentation ici .
jpaugh

Réponses:

149

Supposons que vous ayez une classe appelée MyFancyObjectcomme celle-ci ci-dessous:

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

Il vous permet de tourner:

String ClassName = "MyFancyObject";

Dans

MyFancyObject obj;

En utilisant

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

et peut ensuite faire des choses comme:

obj.A = 100;

C'est son but. Il a également de nombreuses autres surcharges telles que la fourniture d'un Typenom de classe au lieu du nom de classe dans une chaîne. Pourquoi vous auriez un problème comme celui-là est une autre histoire. Voici quelques personnes qui en avaient besoin:

deepee1
la source
2
Cela m’a été utile. Dans mon cas, la classe était dans un espace de noms différent, donc je devais m'assurer que j'avais inclus l'espace de noms dans ma chaîne ClassName (ie String ClassName = "My.Namespace.MyFancyObject";).
Scott
1
Vous avez oublié d'ajouter le Unwrap (). Vous pouvez également mettre null, au lieu de "MyAssembly", et le système recherchera l'assembly actuel.
Tony
1
Puis-je faire quelque chose comme ça obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))mais au lieu de lancer avec le type. Cast avec le type fait à partir du ClassName? Comme ça Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))?
rluks
1
En quoi ce qui précède est-il différent de: MyFancyObject obj = new MyFancyObject?
Ed Landau
1
@Ed Landau La différence est que vous pouvez instancier un objet au moment de l'exécution dont vous ne connaissez pas son type au moment de la compilation. Par exemple, si vous construisez un système de plugins pour votre programme. Les liens dans la réponse pourraient vous donner quelques idées sur quand il ne serait pas possible d'écrire simplement MyFancyObject obj = new MyFancyObject (). Cela serait souvent associé à l'utilisation de la réflexion en général. Vous pouvez également consulter stackoverflow.com/questions/9409293/… pour une autre description.
deepee1
47

Eh bien, je peux vous donner un exemple pourquoi utiliser quelque chose comme ça. Pensez à un jeu où vous souhaitez stocker votre niveau et vos ennemis dans un fichier XML. Lorsque vous analysez ce fichier, vous pouvez avoir un élément comme celui-ci.

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

ce que vous pouvez faire maintenant est de créer dynamiquement les objets trouvés dans votre fichier de niveau.

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

Ceci est très utile pour créer des environnements dynamiques. Bien sûr, il est également possible de l'utiliser pour des scénarios de plugins ou d'addins et bien plus encore.

dowhilefor
la source
5
J'ai compris que c'était pour créer une nouvelle instance d'un type, mais c'est un bel exemple simple de pourquoi on voudrait le faire.
jamiebarrow
14

Mon bon ami MSDN peut vous l'expliquer, avec un exemple

Voici le code au cas où le lien ou le contenu changerait à l'avenir:

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
la source
1
Il est peu probable que MSDN se produise, et copier le contenu ici est presque une violation du droit d'auteur;)
Claus Jørgensen
13
Vous avez raison. Personnellement, je pense que le principe fonctionne également pour donner de meilleures réponses. Je viens souvent à SO avec beaucoup en tête de mon projet actuel. En général, je veux juste une réponse simple et précise, pour que je puisse continuer là où je m'étais arrêté. Je déteste avoir à ouvrir des articles, qui parfois même des liens vers d'autres articles. Beaucoup de répondants ne réalisent pas que beaucoup de gens ne viennent pas ici avec le temps de lire plusieurs articles, avec une tonne d'introductions et de discussions inutiles. Résumer brièvement les parties importantes d'un bon article est la clé de certaines des meilleures réponses que j'ai vues.
Aske B.
10

Vous pouvez également faire ceci -

var handle = Activator.CreateInstance("AssemblyName", 
                "Full name of the class including the namespace and class name");
var obj = handle.Unwrap();
William
la source
Le Unwrap () était la pièce manquante. :)
Tony
Je ne trouve pas la méthode 'Unwrap ()' ci-dessus à utiliser (comme ci-dessus). Quelque chose a-t-il changé dans les nouvelles API .NET?
Sam
Il est toujours là dans l'espace de noms System.
William
2
Pourriez-vous fournir des explications supplémentaires sur ce que .Unwrap()fait exactement et comment cela se rapporte à d'autres solutions?
Jeroen Vannevel
@Sam est sur une surcharge différente de l' CreateInstanceendroit où il revient System.Runtime.Remoting.ObjectHandle.
nawfal
9

Un bon exemple pourrait être le suivant: par exemple, vous avez un ensemble de Loggers et vous autorisez l'utilisateur à spécifier le type à utiliser dans le runtime via le fichier de configuration.

Ensuite:

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

OU un autre cas est celui où vous avez une fabrique d'entités communes, qui crée une entité, et est également responsable de l'initialisation d'une entité par les données reçues de DB:

(pseudocode)

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
la source
Cela ne fonctionne pas, typeof(loggerType)résultatsloggerType is a variable and used like a type
Barkermn01
3

le Activator.CreateInstance méthode crée une instance d'un type spécifié à l'aide du constructeur qui correspond le mieux aux paramètres spécifiés.

Par exemple, disons que vous avez le nom du type sous forme de chaîne et que vous souhaitez utiliser la chaîne pour créer une instance de ce type. Vous pouvez utiliser Activator.CreateInstancepour cela:

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

Voici un article MSDN qui explique son application plus en détail:

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

James Johnson
la source
5
Ou vous pouvez simplement utiliser new Foo(). Je pense que le PO voulait un exemple plus réaliste.
Konrad Rudolph le
1
Je suis d'accord avec @Konrad. La raison de l'utilisation CreateInstanceest si vous ne connaissez pas le type d'objet que vous allez instancier au moment du design. Dans cet exemple, vous savez clairement qu'il est de type Foopuisque vous le convertissez en type Foo. Vous ne feriez jamais cela parce que vous pouvez simplement le faire Foo foo = new Foo().
theyetiman
1

En vous basant sur deepee1 et ceci , voici comment accepter un nom de classe dans une chaîne, puis l'utiliser pour lire et écrire dans une base de données avec LINQ. J'utilise "dynamique" au lieu du casting de deepee1 car il me permet d'attribuer des propriétés, ce qui nous permet de sélectionner et d'opérer dynamiquement sur n'importe quelle table que nous voulons.

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
la source
0

Pourquoi l'utiliseriez-vous si vous connaissiez déjà la classe et alliez la lancer? Pourquoi ne pas le faire à l'ancienne et faire de la classe comme vous le faites toujours? Il n'y a aucun avantage à cela sur la façon dont cela se fait normalement. Existe-t-il un moyen de prendre le texte et de l'utiliser ainsi:

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

Si je sais déjà que c'est une pizza, il n'y a aucun avantage à:

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

mais je vois un énorme avantage à la méthode Magic si elle existe.

user8659016
la source
0

Couplé à la réflexion, j'ai trouvé Activator.CreateInstance très utile pour mapper le résultat d'une procédure stockée à une classe personnalisée, comme décrit dans la réponse suivante .

utileBee
la source