Призначення Activator.CreateInstance з прикладом?


124

Чи може хтось Activator.CreateInstance()детально пояснити мету?


6
Можливо, більша документація та приклад відсутні в загальних перевантаженнях. Я б закликав, щоб документація CreateInstance(Type type)звідповідала CreateInstance<T>()перевантаженню.
Клаус Йоргенсен

4
На сторінці MSDN є лише один пояснювальний рядок: "Містить методи для створення типів об'єктів локально чи віддалено або отримання посилань на існуючі віддалені об'єкти. Цей клас не може бути успадкований". msdn.microsoft.com/en-us/library/system.activator.aspx
gonzobrains

2
Якщо ви приїхали сюди з фону Java, це такий c#.netспосіб Object xyz = Class.forName(className).newInstance();.
SNag

Там краще документація для цього тут .
jpaugh

Відповіді:


149

Скажімо, у вас є клас, який називається MyFancyObjectтаким:

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

Це дозволяє вам повернути:

String ClassName = "MyFancyObject";

В

MyFancyObject obj;

Використання

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

а потім можна робити такі речі, як:

obj.A = 100;

Це його призначення. Він також має багато інших перевантажень, таких як надання Typeзамість імені класу в рядку. Чому у вас виникне така проблема, це вже інша історія. Ось деякі люди, яким це було потрібно:


2
Це виявилося мені корисним. У моєму випадку клас знаходився в іншому просторі імен, тому я повинен був переконатися, що я включив простір імен у свій рядок ClassName (тобто String ClassName = "My.Namespace.MyFancyObject";).
Скотт

1
Ви забули додати Unwrap (). Ви також можете поставити null замість "MyAssembly", і система буде шукати поточну збірку.
Тоні

1
Чи можу я зробити щось подібне, obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))але замість того, щоб робити кастинг із типом. У ролях із типом, зробленим із ClassName? Як це Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))?
rluks

1
Чим вищезгадане відрізняється від: MyFancyObject obj = new MyFancyObject?
Ед Ландау

1
@Ed Landau Різниця полягає в тому, що ви можете інстанціювати об'єкт під час виконання, який ви не знаєте, що це тип під час компіляції. Наприклад, якщо ви будували систему плагінів для вашої програми. Посилання у відповіді можуть дати вам декілька ідей щодо того, коли неможливо було просто написати MyFancyObject obj = new MyFancyObject (). Це часто поєднується із застосуванням рефлексії взагалі. Ви також можете перевірити stackoverflow.com/questions/9409293/… для ще одного опису.
deepee1

47

Ну я можу навести вам приклад, чому використовувати щось подібне. Придумайте гру, де ви хочете зберігати свій рівень та ворогів у XML-файлі. Коли ви аналізуєте цей файл, у вас може виникнути такий елемент.

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

тепер ви можете зробити динамічне створення об'єктів, знайдених у вашому файлі рівня.

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

Це дуже корисно для побудови динамічних середовищ. Звичайно, можна також використовувати це для сценаріїв плагінів або додатків та багато іншого.


5
Я зрозумів, що це створити новий екземпляр типу, але це приємний простий приклад того, чому б хотілося це зробити.
jamiebarrow

14

Мій хороший друг MSDN може пояснити це вам на прикладі

Ось код на випадок, якщо посилання або вміст змінюються в майбутньому:

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
Навряд чи це станеться для MSDN, і копіювання вмісту тут майже є порушенням авторських прав;)
Claus Jørgensen

13
Ти маєш рацію. Особисто я відчуваю, що принцип також працює, щоб дати кращі відповіді. Я часто приходжу до SO з великим розумом від свого поточного проекту. Зазвичай я просто хочу просту і точну відповідь, щоб я міг продовжувати там, де зупинився. Я ненавиджу відкривати статті, які іноді навіть посилаються на інші статті. Багато людей, які відповідають, не усвідомлюють, що багато людей не приходять сюди з часом на руку, щоб прочитати кілька статей, маючи багато непотрібних вступів та бесід Короткий підсумок важливих частин хорошої статті є ключовим для деяких найкращих відповідей, які я бачив.
Aske B.

10

Ви також можете це зробити -

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

Unwrap () був відсутнім твором. :)
Тоні

Я не можу знайти метод "Unwrap ()" для використання (як вище). Щось змінилося в нових API .NET?
Сем

Він все ще є в просторі імен системи.
Вільям

2
Не могли б ви надати додаткові пояснення щодо того, .Unwrap()що саме робить, і як це стосується інших рішень?
Єроен Ванневель

@Sam знаходиться на іншому перевантаженні того місця, CreateInstanceде він повертається System.Runtime.Remoting.ObjectHandle.
nawfal

9

Хорошим прикладом може бути наступний: наприклад, у вас є набір Loggers, і ви можете користувачеві вказати тип, який буде використовуватися під час виконання через файл конфігурації.

Тоді:

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

АБО інший випадок, коли у вас є загальна фабрика сутностей, яка створює сутність, а також відповідає за ініціалізацію об'єкта за даними, отриманими з БД:

(псевдокод)

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

Це не працює, typeof(loggerType)результатиloggerType is a variable and used like a type
Barkermn01

3

Activator.CreateInstanceМетод створює екземпляр зазначеного типу , використовуючи конструктор , який найкращим чином відповідає зазначеним параметрам.

Наприклад, скажімо, що у вас є ім'я типу у вигляді рядка, і ви хочете використовувати рядок для створення примірника цього типу. Ви можете використовувати Activator.CreateInstanceдля цього:

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

Ось стаття MSDN, яка детальніше пояснює його застосування:

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


5
Або ви могли просто скористатися new Foo(). Я думаю, що ОП хотіла більш реалістичного прикладу.
Конрад Рудольф

1
Я згоден з @Konrad. Причиною використання CreateInstanceє те, що ви не знаєте тип об'єкта, який ви збираєтесь інстанціювати під час проектування. У цьому прикладі ви чітко знаєте, що він має тип, Fooоскільки ви передаєте його як тип Foo. Ви ніколи цього не зробите, бо можете просто зробити Foo foo = new Foo().
onietiman

1

Побудова з deepee1 і цього , ось як прийняти ім’я класу в рядку, а потім використовувати його для читання і запису в базу даних з LINQ. Я використовую "динамічний" замість кастингу deepee1, тому що він дозволяє мені призначати властивості, що дозволяє нам динамічно вибирати та працювати з будь-якою таблицею, яку ми хочемо.

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

Навіщо використовувати його, якщо ви вже знали клас і збиралися його віддати? Чому б просто не зробити це старомодним способом і зробити клас таким, як ви завжди його складаєте? У цьому немає переваги в тому, як це робиться нормально. Чи є спосіб взяти текст і оперувати ним таким чином:

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

Якщо я вже знаю його піцу, немає переваги:

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

але я бачу величезну перевагу методу Magic, якщо він існує.


0

У поєднанні з відображенням я виявив, що Activator.CreateInstance дуже корисний для відображення результатів збереженої процедури до користувацького класу, як описано в наступній відповіді .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.