Memuat DLL saat runtime di C #

91

Saya mencoba untuk mencari tahu bagaimana Anda bisa pergi tentang mengimpor dan menggunakan .dll saat runtime di dalam aplikasi C #. Menggunakan Assembly.LoadFile () Saya telah berhasil membuat program saya memuat dll (bagian ini pasti berfungsi karena saya bisa mendapatkan nama kelas dengan ToString ()), namun saya tidak dapat menggunakan 'Output' metode dari dalam aplikasi konsol saya. Saya mengompilasi .dll kemudian memindahkannya ke proyek konsol saya. Apakah ada langkah ekstra antara CreateInstance dan kemudian dapat menggunakan metode?

Ini adalah kelas di DLL saya:

namespace DLL
{
    using System;

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

dan berikut adalah aplikasi yang ingin saya muat 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();
        }
    }
}
danbroooks
sumber

Jawaban:

128

Anggota harus dapat dipecahkan pada waktu kompilasi untuk dipanggil langsung dari C #. Jika tidak, Anda harus menggunakan objek refleksi atau dinamis.

Refleksi

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

Dinamis (.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();
        }
    }
}
Falcon Gelap
sumber
12
Perhatikan bahwa ini mencoba memanggil Outputsetiap jenis dalam rakitan, yang kemungkinan besar akan terlempar sebelum kelas "benar" ditemukan ...
Reed Copsey
1
@ReedCopsey, Setuju, tetapi untuk contoh sederhananya, tipenya adalah satu-satunya yang terlihat. "Satu-satunya tipe yang terlihat di luar rakitan adalah tipe publik dan tipe publik bersarang dalam tipe publik lainnya." Untuk contoh yang tidak sepele, jelas ini akan menjadi masalah ...
Dark Falcon
1
Rapi dengan dua contoh! :)
Niels Abildgaard
22
Inilah sebabnya mengapa antarmuka sering digunakan dan Anda dapat melakukan deteksi fitur seperti IDog dog = someInstance as IDog;dan menguji apakah nilainya nol. Letakkan antarmuka Anda di DLL umum yang dibagikan oleh klien, dan plugin apa pun yang akan dimuat secara dinamis harus menerapkan antarmuka itu. Ini kemudian akan membiarkan Anda mengkodekan klien Anda terhadap antarmuka IDog dan memiliki intellisense + pemeriksaan tipe yang kuat pada waktu kompilasi daripada menggunakan dinamis.
AaronLS
1
@ Tarek.Mh: Itu akan membutuhkan ketergantungan waktu kompilasi pada Class1. Pada saat itu Anda bisa menggunakan new Class1(). Penanya secara eksplisit menetapkan dependensi waktu proses. dynamicmemungkinkan program untuk tidak memerlukan ketergantungan waktu kompilasi Class1sama sekali.
Dark Falcon
39

Saat ini, Anda sedang membuat instance dari setiap jenis yang ditentukan di majelis . Anda hanya perlu membuat satu contoh Class1untuk memanggil metode ini:

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();
    }
}
Reed Copsey
sumber
19

Anda perlu membuat instance dari tipe yang mengekspos Outputmetode:

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();
     }
Alberto
sumber
Terima kasih banyak - inilah yang saya cari. Saya tidak percaya ini tidak berperingkat lebih tinggi daripada jawaban lainnya, karena ini menunjukkan penggunaan kata kunci dinamis.
skiphoppy
Ah, sekarang saya melihatnya juga ada di jawaban DarkFalcon. Milik Anda lebih pendek dan membuatnya lebih mudah dilihat. :)
skiphoppy
0

Activator.CreateInstance() mengembalikan sebuah objek, yang tidak memiliki metode Output.

Sepertinya Anda berasal dari bahasa pemrograman dinamis? C # jelas bukan itu, dan apa yang Anda coba lakukan akan sulit.

Karena Anda memuat dll tertentu dari lokasi tertentu, mungkin Anda hanya ingin menambahkannya sebagai referensi untuk aplikasi konsol Anda?

Jika Anda benar-benar ingin memuat assembly melalui Assembly.Load, Anda harus melalui refleksi untuk memanggil anggota mana punc

Sesuatu seperti type.GetMethod("Output").Invoke(c, null);harus melakukannya.

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

Itu memuat semua DLL yang ada di folder yang dapat dieksekusi Anda.

Dalam kasus saya, saya mencoba menggunakan Reflectionuntuk menemukan semua subclass dari suatu kelas, bahkan di DLL lain. Ini berhasil, tetapi saya tidak yakin apakah itu cara terbaik untuk melakukannya.

EDIT: Saya menghitung waktunya, dan sepertinya hanya memuatnya pertama kali.

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

Keluaran: 34 0 0 0

Jadi seseorang berpotensi menjalankan kode itu sebelum pencarian Refleksi untuk berjaga-jaga.

Samuel Cabrera
sumber
-1

Tidak terlalu sulit.

Anda dapat memeriksa fungsi yang tersedia dari objek yang dimuat, dan jika Anda menemukan fungsi yang Anda cari berdasarkan nama, maka mengintip fungsi yang diharapkan, jika ada. Jika itu panggilan yang Anda coba temukan, panggil itu menggunakan metode Invoke objek MethodInfo.

Pilihan lainnya adalah dengan membuat objek eksternal Anda ke sebuah antarmuka, dan mentransmisikan objek yang dimuat ke antarmuka itu. Jika berhasil, panggil fungsi secara native.

Ini hal yang sangat sederhana.

ChrisH
sumber
Wah, belum pasti kenapa suara turun. Saya memiliki aplikasi produksi yang melakukan persis seperti ini selama 12 tahun terakhir. * mengangkat bahu * Siapapun membutuhkan beberapa kode untuk melakukan ini, kirimi saya pesan. Saya akan mengemas sebagian dari kode produksi saya dan mengirimkannya.
ChrisH
10
Saya menduga downvote berkaitan dengan kurangnya contoh dan nada yang memadat ... Sepertinya Anda memiliki dasar untuk jawaban lengkap, jadi jangan takut untuk mengedit lebih detail :)
Shadow
1
Tidak sopan mengatakan "ini hal yang cukup sederhana", dan itulah mengapa Anda mendapatkan suara negatif.
ABPerson
1
Saya tidak bersikap kasar atau merendahkan .... 6 tahun yang lalu. Nada tidak muncul dalam teks, jelas. Itu benar-benar dimaksudkan untuk menjadi sangat ringan hati ... Saya juga benar - benar merasa seperti saya memiliki tautan ke sampel kode di sana selama bertahun-tahun, dan saya tidak tahu ke mana itu pergi (dengan asumsi itu benar-benar ada di sana seperti yang saya ingat ). : \
ChrisH
Saya tidak tahu cara kerja MethodInfo tetapi tampaknya berharga. Saya akan mengatakan jawaban Anda memiliki potensi untuk menjadi lebih baik daripada yang diterima saat ini, tetapi itu harus diselesaikan. Jika Anda pernah melakukannya, itu akan dihargai. Jika demikian, jangan tautkan ke sampel kode. Ini mungkin rusak di masa mendatang. Yang terbaik adalah memberikan sampel sendiri, dengan kemungkinan link ke sumber atau info tambahan untuk melanjutkan membaca.
SpaghettiCook