Bisakah saya memuat perakitan .NET saat runtime dan instantiate tipe yang hanya mengetahui nama?

178

Apakah mungkin untuk instantiate objek pada saat runtime jika saya hanya memiliki nama DLL dan nama kelas, tanpa menambahkan referensi ke rakitan di proyek? Kelas mengimplementasikan antarmuka, jadi setelah saya instantiate kelas, saya kemudian akan melemparkannya ke antarmuka.

Nama majelis:

library.dll

Ketik nama:

Company.Project.Classname


EDIT: Saya tidak memiliki jalur absolut dari DLL, jadi Assembly.LoadFiletidak akan berfungsi. DLL mungkin berada di root aplikasi, system32, atau bahkan dimuat di GAC.

MegaByte
sumber

Jawaban:

221

Iya. Anda perlu menggunakan Assembly.LoadFromuntuk memuat unit ke dalam memori, maka Anda dapat menggunakan Activator.CreateInstanceuntuk membuat instance dari jenis yang Anda inginkan. Anda harus melihat jenisnya terlebih dahulu menggunakan refleksi. Ini adalah contoh sederhana:

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

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

object instanceOfMyType = Activator.CreateInstance(type);

Memperbarui

Ketika Anda memiliki nama file rakitan dan nama tipe, Anda bisa menggunakan Activator.CreateInstance(assemblyName, typeName)untuk meminta resolusi tipe .NET untuk menyelesaikannya menjadi tipe. Anda dapat membungkusnya dengan mencoba / menangkap sehingga jika gagal, Anda dapat melakukan pencarian direktori di mana Anda dapat secara khusus menyimpan majelis tambahan yang jika tidak mungkin tidak akan dicari. Ini akan menggunakan metode sebelumnya pada saat itu.

Jeff Yates
sumber
2
Saya tidak punya jalur absolut dll, jadi assemlby.LoadFile dll. tidak akan bekerja, ada ide lain?
MegaByte
@ rp Selalu senang untuk membantu (dan hanya terlambat satu tahun saat mengatakannya!)
Jeff Yates
2
@MegaByte: LoadFrom berbeda dari LoadFile. Ini akan menyelesaikan dependensi Anda dan harus menyelesaikan nama DLL dari jalur yang dikenal (GAC, direktori exe, dll.) Lihat MSDN untuk informasi lebih lanjut.
Jeff Yates
1
Satu hal lagi ... (saya lagi) Um, Anda tidak bisa hanya memiliki "MyType" sebagai nama tipe, itu harus diikuti oleh NAMESPACE. Jadi ini akan lebih akurat:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@ Cipi: Secara teknis, sebuah tipe adalah nama namespace penuh (konsep namespace adalah kenyamanan bahasa). Anda dapat memiliki tipe tanpa namespace di dalam CLR - Saya baru saja memberikan contoh yang terlalu sederhana.
Jeff Yates
36

Pertimbangkan keterbatasan Load*metode yang berbeda . Dari dokumen MSDN ...

LoadFile tidak memuat file ke dalam konteks LoadFrom, dan tidak menyelesaikan dependensi menggunakan jalur load, seperti metode LoadFrom.

Informasi lebih lanjut tentang Memuat Konteks dapat ditemukan di LoadFromdokumen.

Anthony Mastrean
sumber
19

Activator.CreateInstance seharusnya bekerja.

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

Catatan: Nama jenis harus merupakan jenis yang sepenuhnya memenuhi syarat.

Contoh:

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

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
tvanfosson
sumber
1
Hanya catatan tentang ini: TypeNameharus sepenuhnya memenuhi syarat. Saya harus menyebutnya seperti ini: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") Dan itu mengembalikan sebuah ObjectHandle. Untuk turun ke antarmuka Anda yang perlu Anda lakukanObjectHandle.UnWrap()
Anthony Sottile
7

Saya menemukan pertanyaan ini dan beberapa jawaban sangat berguna, namun saya memang memiliki masalah jalur, jadi jawaban ini akan mencakup memuat perpustakaan dengan menemukan jalur direktori bin.

Solusi pertama:

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

Solusi kedua

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

Anda dapat menggunakan prinsip yang sama untuk antarmuka (Anda akan membuat kelas tetapi melakukan casting ke antarmuka), seperti:

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

Contoh ini untuk aplikasi web tetapi yang serupa dapat digunakan untuk aplikasi Desktop, hanya path yang diselesaikan dengan cara yang berbeda, misalnya

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
sumber
5

Mudah.

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

Berikut ini tautan referensi

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

pengguna3722131
sumber
Itu cara yang mengerikan untuk mendukung pemuatan kode dinamis. MS selalu suka memaksa kami terlalu banyak detail.
jelas
3

Mulai dari Framework v4.5 Anda dapat menggunakan Activator.CreateInstanceFrom () untuk dengan mudah instantiate kelas dalam majelis. Contoh berikut menunjukkan cara menggunakannya dan cara memanggil metode yang melewati parameter dan mendapatkan nilai balik.

    // 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" } ));
afiorillo
sumber
2

Iya. Saya tidak punya contoh yang saya lakukan tersedia secara pribadi saat ini. Saya akan memposting nanti ketika saya menemukan beberapa. Pada dasarnya Anda akan menggunakan refleksi untuk memuat perakitan dan kemudian untuk menarik jenis apa pun yang Anda butuhkan untuk itu.

Sementara itu, tautan ini harus membantu Anda memulai:

Menggunakan refleksi untuk memuat rakitan yang tidak direferensikan saat runtime

Giovanni Galbo
sumber
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
sumber
2

Anda dapat memuat perakitan menggunakan metode * Assembly.Load **. Menggunakan Activator.CreateInstance Anda dapat membuat instance baru dari jenis yang Anda inginkan. Ingatlah bahwa Anda harus menggunakan nama tipe lengkap dari kelas yang ingin Anda muat (misalnya Namespace.SubNamespace.ClassName ). Menggunakan metode InvokeMember dari kelas Type Anda dapat memanggil metode pada tipe.

Juga, perhatikan bahwa setelah dimuat, sebuah perakitan tidak dapat dibongkar sampai seluruh AppDomain diturunkan juga (ini pada dasarnya adalah kebocoran memori).

Dario Solera
sumber
2

Bergantung pada seberapa intrinsik fungsi ini pada proyek Anda, Anda mungkin ingin mempertimbangkan sesuatu seperti MEF yang akan menangani pemuatan dan pengikatan bersama komponen untuk Anda.

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

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

dynamic instanceOfMyType = Activator.CreateInstance(type);

Jadi dengan cara ini Anda dapat menggunakan fungsi tidak dengan mendapatkan methodinfo, dan kemudian memohonnya. Anda akan melakukan seperti instanceOfMyType.MethodName (); Tapi Anda tidak bisa menggunakan Intellisense karena tipe dinamis diketik dalam runtime, bukan dalam waktu kompilasi.

David Mkheyan
sumber
1

Ya, itu, Anda akan ingin menggunakan metode Beban statis pada kelas Majelis, lalu panggil kemudian panggil metode CreateInstance pada instance Majelis yang dikembalikan kepada Anda dari panggilan ke Load.

Selain itu, Anda dapat memanggil salah satu metode statis lain yang dimulai dengan "Load" pada kelas Assembly, tergantung pada kebutuhan Anda.

casperOne
sumber
0

Anda dapat melakukan ini dengan cara ini:

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);
Pankaj
sumber