Zweck von Activator.CreateInstance mit Beispiel?

124

Kann jemand den Activator.CreateInstance()Zweck im Detail erklären ?

Tabriz Atayi
quelle
6
Wahrscheinlich ist die umfassendere Dokumentation und das Beispiel in den generischen Überladungen nicht verfügbar. Ich würde empfehlen, dass die Dokumentation von CreateInstance(Type type)mit der CreateInstance<T>()Überlastung übereinstimmt .
Claus Jørgensen
4
Die MSDN-Seite enthält nur eine einzige erklärende Zeile: "Enthält Methoden zum lokalen oder Remote-Erstellen von Objekttypen oder zum Abrufen von Verweisen auf vorhandene Remote-Objekte. Diese Klasse kann nicht vererbt werden." msdn.microsoft.com/en-us/library/system.activator.aspx
Gonzobrains
2
Wenn Sie mit einem Java-Hintergrund hierher gekommen sind, ist dies die c#.netrichtige Vorgehensweise Object xyz = Class.forName(className).newInstance();.
SNag
Es gibt eine bessere Dokumentation für sie hier .
Jpaugh

Antworten:

149

Angenommen, Sie haben eine Klasse MyFancyObjectwie die folgende:

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

Damit können Sie drehen:

String ClassName = "MyFancyObject";

In

MyFancyObject obj;

Verwenden von

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

und kann dann Sachen machen wie:

obj.A = 100;

Das ist sein Zweck. Es gibt auch viele andere Überladungen, z. B. die Angabe eines Typeanstelle des Klassennamens in einer Zeichenfolge. Warum Sie so ein Problem haben würden, ist eine andere Geschichte. Hier sind einige Leute, die es brauchten:

deepee1
quelle
2
Das hat sich für mich als nützlich erwiesen. In meinem Fall befand sich die Klasse in einem anderen Namespace, daher musste ich sicherstellen, dass ich den Namespace in meine ClassName-Zeichenfolge (dh String ClassName = "My.Namespace.MyFancyObject";) aufgenommen habe.
Scott
1
Sie haben vergessen, Unwrap () hinzuzufügen. Sie können anstelle von "MyAssembly" auch null setzen, und das System durchsucht die aktuelle Assembly.
Tony
1
Kann ich so etwas machen, obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))aber anstatt mit dem Typ zu gießen. Besetzung mit Typ aus dem Klassennamen? So Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))?
rluks
1
Wie unterscheidet sich das oben Gesagte von: MyFancyObject obj = new MyFancyObject?
Ed Landau
1
@Ed Landau Der Unterschied besteht darin, dass Sie ein Objekt zur Laufzeit instanziieren können, dessen Typ Sie zur Kompilierungszeit nicht kennen. Zum Beispiel, wenn Sie ein Plugin-System für Ihr Programm erstellt haben. Die Links in der Antwort könnten Ihnen einige Ideen geben, wann es nicht möglich wäre, einfach MyFancyObject obj = new MyFancyObject () zu schreiben. Dies war oftmals mit der Verwendung von Reflexion im Allgemeinen verbunden. Weitere Informationen finden Sie unter stackoverflow.com/questions/9409293/… .
deepee1
47

Nun, ich kann Ihnen ein Beispiel geben, warum Sie so etwas verwenden sollten. Stellen Sie sich ein Spiel vor, in dem Sie Ihr Level und Ihre Feinde in einer XML-Datei speichern möchten. Wenn Sie diese Datei analysieren, haben Sie möglicherweise ein solches Element.

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

Jetzt können Sie die in Ihrer Level-Datei gefundenen Objekte dynamisch erstellen.

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

Dies ist sehr nützlich, um dynamische Umgebungen aufzubauen. Natürlich ist es auch möglich, dies für Plugin- oder Addin-Szenarien und vieles mehr zu verwenden.

dowhilefor
quelle
5
Ich habe verstanden, dass es darum ging, eine neue Instanz eines Typs zu erstellen, aber dies ist ein schönes einfaches Beispiel dafür, warum man dies tun möchte.
Jamie Barrow
14

Mein guter Freund MSDN kann es Ihnen anhand eines Beispiels erklären

Hier ist der Code für den Fall, dass sich der Link oder Inhalt in Zukunft ändert:

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
quelle
1
Es ist unwahrscheinlich, dass dies für MSDN passiert, und das Kopieren des Inhalts hier ist fast eine Verletzung des Urheberrechts;)
Claus Jørgensen
13
Du hast recht. Persönlich denke ich, dass das Prinzip auch funktioniert, um bessere Antworten zu geben. Ich komme oft mit viel Gedanken aus meinem aktuellen Projekt zu SO. Normalerweise möchte ich nur eine einfache und präzise Antwort, damit ich dort weitermachen kann, wo ich aufgehört habe. Ich hasse es, Artikel öffnen zu müssen, die manchmal sogar auf andere Artikel verweisen. Vielen Antwortenden ist nicht klar, dass viele Leute nicht mit der Zeit hierher kommen, um mehrere Artikel mit einer Menge unnötiger Einführungen und Gespräche zu lesen. Die wichtigen Teile eines guten Artikels kurz zusammenzufassen, ist der Schlüssel zu einigen der besten Antworten, die ich gesehen habe.
Aske B.
10

Sie können dies auch tun -

var handle = Activator.CreateInstance("AssemblyName", 
                "Full name of the class including the namespace and class name");
var obj = handle.Unwrap();
Wilhelm
quelle
Das Unwrap () war das fehlende Stück. :)
Tony
Ich kann die oben verwendete Methode 'Unwrap ()' nicht finden (wie oben). Hat sich an den neuen .NET-APIs etwas geändert?
Sam
Es ist immer noch im System-Namespace vorhanden.
William
2
Können Sie eine zusätzliche Erklärung dazu geben, was .Unwrap()genau funktioniert und wie dies mit anderen Lösungen zusammenhängt?
Jeroen Vannevel
@ Sam ist es auf einer anderen Überladung von CreateInstancewo es zurückkehrt System.Runtime.Remoting.ObjectHandle.
Nawfal
9

Ein gutes Beispiel könnte das nächste sein: Zum Beispiel haben Sie eine Reihe von Loggern und Sie können dem Benutzer erlauben, den Typ anzugeben, der zur Laufzeit über die Konfigurationsdatei verwendet werden soll.

Dann:

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

ODER Ein anderer Fall ist, wenn Sie eine gemeinsame Entitätsfabrik haben, die eine Entität erstellt und auch für die Initialisierung einer Entität durch von DB empfangene Daten verantwortlich ist:

(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
quelle
Dies funktioniert nicht, typeof(loggerType)führt zuloggerType is a variable and used like a type
Barkermn01
3

Die Activator.CreateInstanceMethode erstellt eine Instanz eines angegebenen Typs mit dem Konstruktor, der den angegebenen Parametern am besten entspricht.

Angenommen, Sie haben den Typnamen als Zeichenfolge und möchten mit der Zeichenfolge eine Instanz dieses Typs erstellen. Sie könnten dafür verwenden Activator.CreateInstance:

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

Hier ist ein MSDN-Artikel, in dem die Anwendung ausführlicher erläutert wird:

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

James Johnson
quelle
5
Oder Sie könnten einfach verwenden new Foo(). Ich denke, das OP wollte ein realistischeres Beispiel.
Konrad Rudolph
1
Ich stimme @Konrad zu. Der Grund für die Verwendung CreateInstanceist, wenn Sie den Objekttyp nicht kennen, den Sie zur Entwurfszeit instanziieren möchten. In diesem Beispiel wissen Sie genau, dass es sich um einen Typ handelt, Fooda Sie ihn als Typ umwandeln Foo. Sie würden dies niemals tun, weil Sie es einfach tun können Foo foo = new Foo().
Theyetiman
1

Aufbauend auf deepee1 und diesem wird hier beschrieben, wie Sie einen Klassennamen in einer Zeichenfolge akzeptieren und ihn dann zum Lesen und Schreiben in eine Datenbank mit LINQ verwenden. Ich verwende "dynamisch" anstelle des Castings von deepee1, da ich damit Eigenschaften zuweisen kann, mit denen wir jede gewünschte Tabelle dynamisch auswählen und bearbeiten können.

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
quelle
0

Warum würdest du es benutzen, wenn du die Klasse bereits kennst und sie besetzen würdest? Warum machst du es nicht einfach auf die altmodische Art und machst die Klasse so, wie du es immer machst? Dies hat keinen Vorteil gegenüber der normalen Vorgehensweise. Gibt es eine Möglichkeit, den Text so zu bearbeiten:

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

Wenn ich bereits weiß, dass es eine Pizza ist, gibt es keinen Vorteil für:

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

Aber ich sehe einen großen Vorteil für die Magic-Methode, wenn sie existiert.

user8659016
quelle
0

In Verbindung mit der Reflexion fand ich Activator.CreateInstance sehr hilfreich, um das Ergebnis einer gespeicherten Prozedur einer benutzerdefinierten Klasse zuzuordnen, wie in der folgenden Antwort beschrieben .

nützlichBee
quelle