กำลังโหลด DLL ที่รันไทม์ใน C #


91

ฉันพยายามหาวิธีที่คุณจะนำเข้าและใช้. dll ที่รันไทม์ภายในแอปพลิเคชัน C # ได้อย่างไร การใช้ Assembly.LoadFile () ฉันจัดการเพื่อให้โปรแกรมของฉันโหลด dll (ส่วนนี้ใช้งานได้แน่นอนเพราะฉันสามารถรับชื่อคลาสด้วย ToString ()) ได้ แต่ฉันไม่สามารถใช้ 'เอาท์พุท' ได้ วิธีการจากภายในแอปพลิเคชันคอนโซลของฉัน ฉันกำลังรวบรวม. dll จากนั้นย้ายไปไว้ในโปรเจ็กต์คอนโซลของฉัน มีขั้นตอนเพิ่มเติมระหว่าง CreateInstance และความสามารถในการใช้วิธีนี้หรือไม่?

นี่คือคลาสใน DLL ของฉัน:

namespace DLL
{
    using System;

    public class Class1
    {
        public void Output(string s)
        {
            Console.WriteLine(s);
        }
    }
}

และนี่คือแอปพลิเคชันที่ฉันต้องการโหลด DLL

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

คำตอบ:


128

สมาชิกจะต้องสามารถแก้ไขได้ในเวลารวบรวมเพื่อให้ถูกเรียกโดยตรงจาก C # มิฉะนั้นคุณต้องใช้วัตถุสะท้อนหรือไดนามิก

การสะท้อนกลับ

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
            }

            Console.ReadLine();
        }
    }
}

ไดนามิก (.NET 4.0)

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                dynamic c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

12
โปรดทราบว่าสิ่งนี้พยายามเรียกOutputทุกประเภทในการชุมนุมซึ่งน่าจะโยนก่อนที่จะพบคลาส "ถูกต้อง" ...
Reed Copsey

1
@ReedCopsey เห็นด้วย แต่สำหรับตัวอย่างง่ายๆของเขาประเภทของเขาคือประเภทเดียวที่มองเห็นได้ "ประเภทเดียวที่มองเห็นได้ภายนอกแอสเซมบลีคือประเภทสาธารณะและประเภทสาธารณะที่ซ้อนอยู่ในประเภทสาธารณะอื่น ๆ " สำหรับตัวอย่างที่ไม่สำคัญเห็นได้ชัดว่านี่จะเป็นปัญหา ...
Dark Falcon

1
เรียบร้อยกับสองตัวอย่าง! :)
Niels Abildgaard

22
นี่คือเหตุผลที่มักใช้อินเทอร์เฟซและคุณสามารถตรวจจับคุณลักษณะเช่นIDog dog = someInstance as IDog;และทดสอบว่าไม่เป็นโมฆะหรือไม่ ใส่อินเทอร์เฟซของคุณใน DLL ทั่วไปที่ไคลเอ็นต์แชร์และปลั๊กอินใด ๆ ที่จะโหลดแบบไดนามิกต้องใช้อินเทอร์เฟซนั้น จากนั้นจะช่วยให้คุณเขียนโค้ดไคลเอ็นต์ของคุณกับอินเทอร์เฟซ IDog และมีการตรวจสอบประเภท Intellisense + ที่แข็งแกร่งในเวลาคอมไพล์แทนที่จะใช้ไดนามิก
AaronLS

1
@ Tarek.Mh: Class1นั่นจะต้องมีการพึ่งพาเวลารวบรวมบน ณ new Class1()จุดที่คุณก็สามารถใช้ ผู้ถามระบุการพึ่งพารันไทม์อย่างชัดเจน dynamicช่วยให้โปรแกรมไม่ต้องการการพึ่งพาเวลาคอมไพล์Class1เลย
Dark Falcon

39

ตอนนี้คุณกำลังสร้างตัวอย่างของประเภทที่กำหนดไว้ในการชุมนุมทุก คุณต้องสร้างเพียงอินสแตนซ์เดียวClass1เพื่อเรียกใช้เมธอด:

class Program
{
    static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var theType = DLL.GetType("DLL.Class1");
        var c = Activator.CreateInstance(theType);
        var method = theType.GetMethod("Output");
        method.Invoke(c, new object[]{@"Hello"});

        Console.ReadLine();
    }
}

19

คุณต้องสร้างอินสแตนซ์ประเภทที่แสดงOutputวิธีการ:

static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var class1Type = DLL.GetType("DLL.Class1");

        //Now you can use reflection or dynamic to call the method. I will show you the dynamic way

        dynamic c = Activator.CreateInstance(class1Type);
        c.Output(@"Hello");

        Console.ReadLine();
     }

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

อ่าตอนนี้ฉันเห็นว่ามันอยู่ในคำตอบของ DarkFalcon เช่นกัน ของคุณสั้นลงและทำให้มองเห็นได้ง่ายขึ้น :)
skiphoppy

0

Activator.CreateInstance() ส่งคืนวัตถุซึ่งไม่มีวิธีการส่งออก

ดูเหมือนว่าคุณมาจากภาษาโปรแกรมแบบไดนามิก? C # ไม่ได้เป็นเช่นนั้นแน่นอนและสิ่งที่คุณพยายามทำจะเป็นเรื่องยาก

เนื่องจากคุณกำลังโหลด dll เฉพาะจากตำแหน่งเฉพาะคุณอาจต้องการเพิ่มเป็นข้อมูลอ้างอิงไปยังแอปพลิเคชันคอนโซล

หากคุณต้องการโหลดแอสเซมบลีอย่างแท้จริงAssembly.Loadคุณจะต้องผ่านการสะท้อนกลับเพื่อเรียกสมาชิกใด ๆc

สิ่งที่ชอบtype.GetMethod("Output").Invoke(c, null);ควรทำ


0
foreach (var f in Directory.GetFiles(".", "*.dll"))
            Assembly.LoadFrom(f);

ซึ่งจะโหลด DLL ทั้งหมดที่มีอยู่ในโฟลเดอร์ปฏิบัติการของคุณ

ในกรณีของฉันฉันพยายามใช้Reflectionเพื่อค้นหาคลาสย่อยทั้งหมดของคลาสแม้แต่ใน DLL อื่น ๆ วิธีนี้ได้ผล แต่ฉันไม่แน่ใจว่าเป็นวิธีที่ดีที่สุดหรือไม่

แก้ไข: ฉันหมดเวลาแล้วและดูเหมือนว่าจะโหลดในครั้งแรกเท่านั้น

Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 4; i++)
{
    stopwatch.Restart();
    foreach (var f in Directory.GetFiles(".", "*.dll"))
        Assembly.LoadFrom(f);
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
}

เอาต์พุต: 34 0 0 0

ดังนั้นใคร ๆ ก็สามารถเรียกใช้โค้ดนั้นได้ก่อนการค้นหา Reflection ในกรณีนี้


-1

มันไม่ยากเลย

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

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

นี่เป็นสิ่งที่ค่อนข้างง่าย


ว้าวไม่แน่ใจว่าทำไมโหวตลง ฉันมีแอปพลิเคชันการผลิตที่ทำเช่นนี้ตลอด 12 ปีที่ผ่านมา * ยัก * ใครต้องการรหัสในการทำสิ่งนี้ส่งข้อความมาหาฉัน ฉันจะจัดแพคเกจรหัสการผลิตบางส่วนแล้วส่งไป
ChrisH

10
ฉันสงสัยว่าการโหวตลดลงจะเกี่ยวข้องกับการไม่มีตัวอย่างและโทนการควบแน่น ... ดูเหมือนว่าคุณจะมีพื้นฐานสำหรับคำตอบที่สมบูรณ์ดังนั้นอย่ากลัวที่จะแก้ไขในรายละเอียดเพิ่มเติม :)
Shadow

1
มันเป็นการหยาบคายที่จะพูดว่า "นี่เป็นเรื่องธรรมดา ๆ " และนั่นเป็นสาเหตุที่ทำให้คุณได้รับการโหวตต่ำ
ABPerson

1
ฉันไม่ได้หยาบคายหรืออวดดี .... 6 ปีที่แล้ว โทนเสียงไม่ผ่านข้อความอย่างชัดเจน มันควรจะเป็นเรื่องที่ค่อนข้างเบาจริงๆ ... ฉันรู้สึกเหมือนมีลิงค์ไปยังตัวอย่างโค้ดในนั้นตลอดหลายปีที่ผ่านมาและฉันไม่รู้ว่ามันไปไหน (สมมติว่ามันอยู่ที่นั่นจริงๆเหมือนฉันจำได้ ). : \
ChrisH

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