ฉันสามารถโหลดแอสเซมบลี. NET ที่รันไทม์และสร้างอินสแตนซ์ของชนิดที่รู้ชื่อเท่านั้นได้หรือไม่


178

เป็นไปได้ที่จะยกตัวอย่างวัตถุที่รันไทม์ถ้าฉันมีชื่อ DLL และชื่อคลาสโดยไม่ต้องเพิ่มการอ้างอิงไปยังแอสเซมบลีในโครงการหรือไม่ คลาสใช้อินเทอร์เฟซดังนั้นเมื่อฉันสร้างอินสแตนซ์ของคลาสแล้วฉันจะส่งไปยังอินเทอร์เฟซ

ชื่อการประกอบ:

Library.dll

ชื่อประเภท:

Company.Project.Classname


แก้ไข:ฉันไม่มีเส้นทางที่แน่นอนของ DLL ดังนั้นAssembly.LoadFileจะไม่ทำงาน DLL อาจอยู่ในรูทแอปพลิเคชัน, system32 หรือแม้กระทั่งโหลดใน GAC

คำตอบ:


221

ใช่. คุณต้องใช้Assembly.LoadFromเพื่อโหลดแอสเซมบลีลงในหน่วยความจำจากนั้นคุณสามารถใช้Activator.CreateInstanceเพื่อสร้างอินสแตนซ์ของชนิดที่คุณต้องการ คุณจะต้องค้นหาประเภทก่อนโดยใช้การสะท้อน นี่คือตัวอย่างง่ายๆ:

Assembly assembly = Assembly.LoadFrom("MyNice.dll");

Type type = assembly.GetType("MyType");

object instanceOfMyType = Activator.CreateInstance(type);

ปรับปรุง

เมื่อคุณมีชื่อไฟล์แอสเซมบลีและชื่อประเภทคุณสามารถใช้Activator.CreateInstance(assemblyName, typeName)เพื่อขอความละเอียดของ. NET ชนิดการแก้ไขที่เป็นชนิด คุณสามารถสรุปได้ด้วยการลอง / จับเพื่อที่ว่าถ้าล้มเหลวคุณสามารถทำการค้นหาไดเรกทอรีที่คุณอาจเก็บแอสเซมบลีเพิ่มเติมโดยเฉพาะซึ่งอาจไม่สามารถค้นหาได้ นี่จะใช้วิธีการก่อนหน้านี้ ณ จุดนั้น


2
ฉันไม่มีเส้นทางที่แน่นอนของ dll ดังนั้น assemlby.LoadFile ect ทำงานได้หรือไม่ความคิดอื่น ๆ
MegaByte

@rp เสมอยินดีที่จะช่วยเหลือ (และปลายปีในการบอกเลย!)
เจฟฟ์เยตส์

2
@MegaByte: LoadFrom แตกต่างจาก LoadFile มันจะแก้ไขการอ้างอิงของคุณและควรแก้ไขชื่อ DLL จากเส้นทางที่รู้จัก (GAC, ไดเรกทอรี exe ฯลฯ ) ดู MSDN สำหรับข้อมูลเพิ่มเติม
Jeff Yates

1
อีกอย่างหนึ่ง ... (ฉันอีกครั้ง) อืมคุณไม่สามารถมี "MyType" เป็นชื่อประเภทได้มันต้องตามด้วย NAMESPACE ดังนั้นนี่จะแม่นยำยิ่งขึ้น:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi

1
@Cipi: ในทางเทคนิคประเภทคือชื่อแบบเต็มเนมสเปซ (แนวคิดของเนมสเปซคือความสะดวกด้านภาษา) คุณสามารถมีประเภทที่ไม่มีเนมสเปซภายใน CLR - ฉันเพิ่งจะให้ตัวอย่างที่ง่ายเกินไป
Jeff Yates

36

พิจารณาข้อ จำกัด ของLoad*วิธีการต่างๆ จากเอกสารMSDN ...

LoadFile ไม่โหลดไฟล์ลงในบริบท LoadFrom และไม่สามารถแก้ไขการอ้างอิงโดยใช้เส้นทางการโหลดเช่นเดียวกับวิธี LoadFrom

ข้อมูลเพิ่มเติมเกี่ยวกับโหลดบริบทสามารถพบได้ในLoadFromเอกสาร


19

Activator.CreateInstanceควรใช้งานได้

IFace object = (IFace)Activator.CreateInstance( "AssemblyName",
                                                "TypeName" )
                               .Unwrap();

หมายเหตุ:ชื่อประเภทจะต้องเป็นประเภทที่ผ่านการรับรอง

ตัวอย่าง:

var aray = (IList)Activator.CreateInstance("mscorlib","System.Collections.ArrayList").Unwrap();
aray.Add(10);

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}

1
เพียงแค่ทราบเกี่ยวกับเรื่องนี้: TypeNameจะต้องมีคุณสมบัติครบถ้วน ผมต้องเรียกสิ่งนี้ว่าชอบและที่ส่งกลับActivator.CreateInstance("MyAssembly","MyAssembly.TypeName") ObjectHandleหากต้องการลงไปยังอินเทอร์เฟซของคุณคุณต้องทำObjectHandle.UnWrap()
Anthony Sottile

7

ฉันพบคำถามนี้และคำตอบมีประโยชน์มาก แต่ฉันมีปัญหาเส้นทางดังนั้นคำตอบนี้จะครอบคลุมการโหลดไลบรารีโดยการค้นหาเส้นทางไดเรกทอรี bin

วิธีแก้ปัญหาแรก:

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type T = assembly.GetType("Company.Project.Classname");
Company.Project.Classname instance = (Company.Project.Classname) Activator.CreateInstance(T);

ทางออกที่สอง

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFile(assemblyPath);
(Company.Project.Classname) instance = (Company.Project.Classname) assembly.CreateInstance("Company.Project.Classname");

คุณสามารถใช้หลักการเดียวกันสำหรับอินเทอร์เฟซ (คุณจะสร้างคลาส แต่แคสต์อินเทอร์เฟซ) เช่น:

(Company.Project.Interfacename) instance = (Company.Project.Interfacename) assembly.CreateInstance("Company.Project.Classname");

ตัวอย่างนี้มีไว้สำหรับเว็บแอปพลิเคชัน แต่สามารถใช้คล้ายกับแอปพลิเคชันเดสก์ท็อปได้เท่านั้นเส้นทางจะได้รับการแก้ไขด้วยวิธีที่ต่างกัน

Path.GetDirectoryName(Application.ExecutablePath)

5

มันเป็นเรื่องง่าย.

ตัวอย่างจาก MSDN:

public static void Main()
{
    // Use the file name to load the assembly into the current
    // application domain.
    Assembly a = Assembly.Load("example");
    // Get the type to use.
    Type myType = a.GetType("Example");
    // Get the method to call.
    MethodInfo myMethod = myType.GetMethod("MethodA");
    // Create an instance.
    object obj = Activator.CreateInstance(myType);
    // Execute the method.
    myMethod.Invoke(obj, null);
}

นี่คือลิงค์อ้างอิง

https://msdn.microsoft.com/en-us/library/25y1ya39.aspx


นั่นเป็นวิธีที่น่ากลัวในการรองรับการโหลดโค้ดแบบไดนามิก MS ชอบที่จะบังคับให้เราเข้าไปดูรายละเอียดมากเกินไป
ชัดเจน

3

เริ่มจาก Framework v4.5 คุณสามารถใช้Activator.CreateInstanceFrom ()เพื่อยกตัวอย่างคลาสได้อย่างง่ายดายภายในชุดประกอบ ตัวอย่างต่อไปนี้แสดงวิธีใช้และวิธีการเรียกใช้เมธอดผ่านพารามิเตอร์และรับค่าส่งคืน

    // Assuming moduleFileName contains full or valid relative path to assembly    
    var moduleInstance = Activator.CreateInstanceFrom(moduleFileName, "MyNamespace.MyClass");
    MethodInfo mi = moduleInstance.Unwrap().GetType().GetMethod("MyMethod");
    // Assuming the method returns a boolean and accepts a single string parameter
    bool rc = Convert.ToBoolean(mi.Invoke(moduleInstance.Unwrap(), new object[] { "MyParamValue" } ));

2

ใช่. ฉันไม่มีตัวอย่างที่ฉันได้ทำไว้เป็นการส่วนตัวแล้วในตอนนี้ ฉันจะโพสต์ในภายหลังเมื่อฉันพบบางอย่าง โดยทั่วไปคุณจะใช้การสะท้อนเพื่อโหลดแอสเซมบลีแล้วดึงประเภทใดก็ตามที่คุณต้องการ

ในระหว่างนี้ลิงก์นี้ควรให้คุณเริ่มต้น:

การใช้การไตร่ตรองเพื่อโหลดแอสเซมบลีที่ไม่มีการอ้างอิงที่รันไทม์



2

คุณสามารถโหลดแอสเซมบลีโดยใช้วิธี * Assembly.Load ** การใช้Activator.CreateInstanceคุณสามารถสร้างอินสแตนซ์ใหม่ของประเภทที่คุณต้องการ โปรดทราบว่าคุณต้องใช้ชื่อเต็มของคลาสที่คุณต้องการโหลด (เช่นNamespace.SubNamespace.ClassName ) การใช้เมธอดInvokeMemberของคลาสTypeคุณสามารถเรียกใช้เมธอดกับชนิด

นอกจากนี้ให้คำนึงถึงว่าเมื่อโหลดแล้วจะไม่สามารถยกเลิกการโหลดแอสเซมบลีได้จนกว่า AppDomain ทั้งหมดจะถูกยกเลิกการโหลดเช่นกัน


2

คุณอาจต้องการพิจารณาบางสิ่งบางอย่างเช่นMEFซึ่งจะดูแลการโหลดและการรวมส่วนประกอบต่างๆให้คุณ


2
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

Type type = assembly.GetType("MyType");

dynamic instanceOfMyType = Activator.CreateInstance(type);

ดังนั้นด้วยวิธีนี้คุณสามารถใช้ฟังก์ชั่นที่ไม่ได้รับ methodinfo แล้วเรียกมันคุณจะทำเช่นนี้ instanceOfMyType.MethodName (); แต่คุณไม่สามารถใช้ Intellisense ได้เนื่องจากมีการพิมพ์ประเภทไดนามิกในรันไทม์ไม่ใช่ในเวลารวบรวม


1

ใช่มันคือคุณจะต้องใช้วิธีการโหลดแบบคงที่ในชั้นเรียนประกอบแล้วโทรแล้วเรียกวิธีการ CreateInstance ในอินสแตนซ์การชุมนุมที่กลับมาให้คุณจากการเรียกร้องให้โหลด

นอกจากนี้คุณสามารถเรียกหนึ่งในวิธีคงที่อื่น ๆ ที่เริ่มต้นด้วย "โหลด" ในชั้นเรียนประกอบขึ้นอยู่กับความต้องการของคุณ


0

คุณสามารถทำสิ่งนี้ด้วยวิธีนี้:

using System.Reflection;

Assembly MyDALL = Assembly.Load("DALL"); //DALL name of your assembly
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // name of your class
 object  obj = Activator.CreateInstance(MyLoadClass);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.