วัตถุประสงค์ของ Activator.CreateInstance พร้อมตัวอย่าง?


124

ใครช่วยอธิบายActivator.CreateInstance()วัตถุประสงค์โดยละเอียดได้ไหม


6
อาจเป็นไปได้ว่าเอกสารและตัวอย่างที่มากกว่านี้ไม่มีอยู่ในโอเวอร์โหลดทั่วไป ฉันขอแนะนำให้เอกสารประกอบจากCreateInstance(Type type)ตรงกับการCreateInstance<T>()โอเวอร์โหลด
Claus Jørgensen

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

1
คุณลืมเพิ่ม Unwrap () คุณยังสามารถใส่ null แทน "MyAssembly" และระบบจะค้นหา Assembly ปัจจุบัน
Tony

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 อย่างไร?
Ed Landau

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

สิ่งนี้มีประโยชน์มากสำหรับการสร้างสภาพแวดล้อมแบบไดนามิก แน่นอนว่ามันเป็นไปได้ที่จะใช้สิ่งนี้สำหรับสถานการณ์ปลั๊กอินหรือ addin และอื่น ๆ อีกมากมาย


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

14
คุณถูก. โดยส่วนตัวแล้วฉันรู้สึกว่าหลักการทำงานเพื่อให้คำตอบที่ดีกว่าด้วย ฉันมักจะมาที่ 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 () เป็นชิ้นส่วนที่หายไป :)
Tony

ฉันไม่พบเมธอด 'Unwrap ()' ด้านบนที่จะใช้ (ตามด้านบน) NET APIs ใหม่มีการเปลี่ยนแปลงหรือไม่
Sam

ยังคงมีอยู่ในเนมสเปซของระบบ
วิลเลียม

2
คุณช่วยให้คำอธิบายเพิ่มเติมเกี่ยวกับสิ่งที่.Unwrap()แม่นยำและเกี่ยวข้องกับโซลูชันอื่น ๆ ได้อย่างไร
Jeroen Vannevel

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

หรืออีกกรณีหนึ่งคือเมื่อคุณมีโรงงานเอนทิตีทั่วไปซึ่งสร้างเอนทิตีและรับผิดชอบในการเริ่มต้นเอนทิตีด้วยข้อมูลที่ได้รับจาก 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;
}

สิ่งนี้ใช้ไม่ได้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()หรือคุณก็สามารถใช้ ฉันคิดว่า OP ต้องการตัวอย่างที่เป็นจริงมากขึ้น
Konrad Rudolph

1
ฉันเห็นด้วยกับ @Konrad เหตุผลในการใช้CreateInstanceคือถ้าคุณไม่ทราบประเภทของวัตถุที่คุณจะสร้างอินสแตนซ์ในเวลาออกแบบ ในตัวอย่างนี้คุณรู้อย่างชัดเจนว่ามันเป็นประเภทตั้งแต่คุณหล่อมันเป็นประเภทFoo คุณจะไม่ทำเช่นนี้เพราะคุณก็สามารถทำได้Foo Foo foo = new Foo()
themetiman

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 จะเป็นประโยชน์อย่างมากในการทำแผนที่ที่จัดเก็บผลขั้นตอนไปเรียนที่กำหนดเองตามที่อธิบายไว้ในคำตอบต่อไปนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.