Đang tải các tệp DLL trong thời gian chạy trong C #


91

Tôi đang cố gắng tìm ra cách bạn có thể nhập và sử dụng .dll trong thời gian chạy bên trong ứng dụng C #. Sử dụng Assembly.LoadFile () Tôi đã quản lý để chương trình của mình tải dll (phần này chắc chắn đang hoạt động vì tôi có thể lấy tên của lớp bằng ToString ()), tuy nhiên tôi không thể sử dụng 'Đầu ra' từ bên trong ứng dụng bảng điều khiển của tôi. Tôi đang biên dịch .dll sau đó chuyển nó vào dự án bảng điều khiển của tôi. Có một bước bổ sung giữa CreateInstance và sau đó có thể sử dụng các phương thức không?

Đây là lớp trong DLL của tôi:

namespace DLL
{
    using System;

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

và đây là ứng dụng tôi muốn tải 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();
        }
    }
}

Câu trả lời:


128

Các thành viên phải có thể phân giải được tại thời điểm biên dịch để được gọi trực tiếp từ C #. Nếu không, bạn phải sử dụng đối tượng phản chiếu hoặc động.

Suy ngẫm

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

Động (.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
Lưu ý rằng cố gắng này để gọi Outputtrên tất cả các loại trong lắp ráp, mà nhiều khả năng sẽ ném trước lớp "quyền" được tìm thấy ...
Reed Copsey

1
@ReedCopsey, Đồng ý, nhưng đối với ví dụ đơn giản của anh ấy, loại của anh ấy là loại duy nhất được nhìn thấy. "Các kiểu duy nhất hiển thị bên ngoài một assembly là kiểu công khai và kiểu công khai được lồng trong các kiểu công khai khác." Đối với một ví dụ không tầm thường, rõ ràng đây sẽ là một vấn đề ...
Tối Falcon

1
Gọn gàng với hai ví dụ! :)
Niels Abildgaard

22
Đây là lý do tại sao các giao diện thường được sử dụng và bạn có thể thực hiện phát hiện tính năng chẳng hạn như IDog dog = someInstance as IDog;và kiểm tra nếu nó không phải là null. Đặt các giao diện của bạn trong một DLL chung được chia sẻ bởi khách hàng và bất kỳ plugin nào sẽ được tải động đều phải triển khai giao diện đó. Sau đó, điều này sẽ cho phép bạn viết mã ứng dụng khách của mình dựa trên giao diện IDog và kiểm tra kiểu intellisense + strong tại thời điểm biên dịch thay vì sử dụng động.
AaronLS

1
@ Tarek.Mh: Điều đó sẽ yêu cầu thời gian biên dịch phụ thuộc vào Class1. Tại thời điểm đó bạn chỉ có thể sử dụng new Class1(). Người hỏi đã chỉ định rõ ràng một phụ thuộc thời gian chạy. dynamiccho phép chương trình không yêu cầu phụ thuộc vào thời gian biên dịch Class1.
Dark Falcon

39

Ngay bây giờ, bạn đang tạo một thể hiện của mọi kiểu được xác định trong assembly . Bạn chỉ cần tạo một phiên bản duy nhất Class1để gọi phương thức:

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

Bạn cần tạo một phiên bản của kiểu hiển thị Outputphương thức:

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

Cảm ơn bạn rất nhiều - đây chỉ là những gì tôi đang tìm kiếm. Tôi không thể tin rằng câu trả lời này không được xếp hạng cao hơn các câu trả lời khác, vì nó cho thấy việc sử dụng từ khóa động.
jumphoppy

Ah, bây giờ tôi thấy nó cũng có trong câu trả lời của DarkFalcon. Tuy nhiên, của bạn ngắn hơn và dễ nhìn hơn. :)
jumphoppy

0

Activator.CreateInstance() trả về một đối tượng không có phương thức Đầu ra.

Có vẻ như bạn đến từ các ngôn ngữ lập trình động? C # rõ ràng không phải vậy, và những gì bạn đang cố gắng làm sẽ rất khó khăn.

Vì bạn đang tải một dll cụ thể từ một vị trí cụ thể, có thể bạn chỉ muốn thêm nó làm tham chiếu đến ứng dụng bảng điều khiển của mình?

Nếu bạn thực sự muốn tải lắp ráp thông qua Assembly.Load, bạn sẽ phải đi qua phản chiếu để gọi bất kỳ thành viên nào trênc

Một cái gì đó giống như type.GetMethod("Output").Invoke(c, null);nên làm điều đó.


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

Điều đó sẽ tải tất cả các tệp DLL có trong thư mục tệp thi hành của bạn.

Trong trường hợp của tôi, tôi đang cố gắng sử dụng Reflectionđể tìm tất cả các lớp con của một lớp, ngay cả trong các tệp DLL khác. Điều này đã hiệu quả, nhưng tôi không chắc đó có phải là cách tốt nhất để làm điều đó hay không.

CHỈNH SỬA: Tôi đã hẹn giờ và có vẻ như nó chỉ tải chúng lần đầu tiên.

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

Đầu ra: 34 0 0 0

Vì vậy, người ta có thể chạy mã đó trước khi bất kỳ phản ánh nào tìm kiếm đề phòng.


-1

Nó không quá khó.

Bạn có thể kiểm tra các chức năng có sẵn của đối tượng được tải và nếu bạn tìm thấy đối tượng bạn đang tìm kiếm theo tên, thì hãy rình mò các mệnh giá dự kiến ​​của nó, nếu có. Nếu đó là cuộc gọi bạn đang cố gắng tìm, thì hãy gọi nó bằng phương thức Gọi của đối tượng MethodInfo.

Một tùy chọn khác là chỉ cần xây dựng các đối tượng bên ngoài của bạn thành một giao diện và truyền đối tượng được tải sang giao diện đó. Nếu thành công, hãy gọi hàm nguyên bản.

Đây là công cụ khá đơn giản.


Chà, không chắc tại sao lại bỏ phiếu. Tôi có một ứng dụng sản xuất làm chính xác điều này trong suốt 12 năm qua. * nhún vai * Bất cứ ai cần một số mã để làm điều này, hãy gửi tin nhắn cho tôi. Tôi sẽ đóng gói các phần mã sản xuất của mình và gửi nó cùng.
ChrisH

10
Tôi nghi ngờ downvotes sẽ phải làm gì với việc thiếu các ví dụ và ngưng tụ giai điệu ... Có vẻ như bạn có cơ sở cho một câu trả lời đầy đủ mặc dù, vì vậy đừng ngại chỉnh sửa chi tiết hơn :)
Bóng

1
Thật là thô lỗ khi nói "đây là một thứ khá đơn giản", và đó là lý do tại sao bạn lại nhận được sự phản đối.
ABPerson

1
Tôi không thô lỗ hay trịch thượng .... 6 năm trước. Giọng điệu không xuất hiện trong văn bản, rõ ràng. Nó thực sự có nghĩa là khá nhẹ nhàng ... Tôi cũng thực sự cảm thấy như tôi đã có một liên kết đến một mẫu mã trong đó suốt những năm đó, và tôi không biết nó đã đi đâu (giả sử nó thực sự ở đó như tôi đang nhớ ). : \
ChrisH

Tôi không biết MethodInfo hoạt động như thế nào nhưng nó có vẻ có giá trị. Tôi muốn nói rằng câu trả lời của bạn có khả năng tốt hơn câu trả lời được chấp nhận hiện tại nhưng nó cần được hoàn thành. Nếu bạn từng đến được với nó, nó sẽ được đánh giá cao. Nếu vậy, vui lòng không liên kết đến một mẫu mã. Chúng có thể bị hỏng trong tương lai. Tốt nhất là bạn nên tự cung cấp mẫu, có thể kèm theo liên kết đến nguồn hoặc thông tin bổ sung để đọc tiếp.
SpaghettiCook
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.