¿Puedo cargar un ensamblado .NET en tiempo de ejecución e instanciar un tipo conociendo solo el nombre?

178

¿Es posible crear una instancia de un objeto en tiempo de ejecución si solo tengo el nombre de la DLL y el nombre de la clase, sin agregar una referencia al ensamblado en el proyecto? La clase implementa una interfaz, así que una vez que instancia la clase, la lanzaré a la interfaz.

Nombre de la asamblea:

library.dll

Escribe un nombre:

Company.Project.Classname


EDITAR: no tengo la ruta absoluta de la DLL, por Assembly.LoadFilelo que no funcionará. La DLL puede estar en la raíz de la aplicación, system32 o incluso cargada en el GAC.

Megabyte
fuente

Respuestas:

221

Si. Debe usar Assembly.LoadFrompara cargar el ensamblado en la memoria, luego puede usar Activator.CreateInstancepara crear una instancia de su tipo preferido. Tendrá que buscar primero el tipo usando la reflexión. Aquí hay un ejemplo simple:

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

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

object instanceOfMyType = Activator.CreateInstance(type);

Actualizar

Cuando tenga el nombre del archivo de ensamblaje y el nombre del tipo, puede usarlo Activator.CreateInstance(assemblyName, typeName)para pedirle a la resolución de tipo .NET que lo resuelva en un tipo. Podría envolverlo con un try / catch para que, si falla, pueda realizar una búsqueda de directorios donde puede almacenar específicamente ensamblados adicionales que de otro modo no podrían buscarse. Esto usaría el método anterior en ese punto.

Jeff Yates
fuente
2
No tengo la ruta absoluta de la dll, por lo que assemlby.LoadFile ect. no funciona, alguna otra idea?
MegaByte
@rp Siempre dispuesto a ayudar (¡y solo un año tarde en decirlo!)
Jeff Yates
2
@MegaByte: LoadFrom es diferente a LoadFile. Resolverá sus dependencias y debería resolver el nombre de DLL de las rutas conocidas (GAC, directorio exe, etc.) Consulte MSDN para obtener más información.
Jeff Yates
1
Una cosa más ... (yo otra vez) Um, no puedes simplemente tener "MyType" como nombre del tipo, debe ser seguido por NAMESPACE. Entonces esto sería más exacto:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@Cipi: Técnicamente, un tipo es su nombre de espacio de nombres completo (el concepto de espacio de nombres es un lenguaje conveniente). Puede tener un tipo sin espacio de nombres dentro del CLR: solo estaba proporcionando un ejemplo demasiado simplificado.
Jeff Yates
36

Considere las limitaciones de los diferentes Load*métodos. De los documentos de MSDN ...

LoadFile no carga archivos en el contexto LoadFrom, y no resuelve dependencias utilizando la ruta de carga, como lo hace el método LoadFrom.

Puede encontrar más información sobre Contextos de carga en el LoadFrom documentos.

Anthony Mastrean
fuente
19

Activator.CreateInstance debería funcionar.

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

Nota: El nombre del tipo debe ser el tipo totalmente calificado.

Ejemplo:

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

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
tvanfosson
fuente
1
Solo una nota sobre esto: TypeNamedebe estar completamente calificado. Tuve que llamar a esto como: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") Y eso devuelve un ObjectHandle. Para acceder a su interfaz, debe hacerloObjectHandle.UnWrap()
Anthony Sottile
7

Encontré esta pregunta y algunas respuestas muy útiles, sin embargo, tuve problemas de ruta, por lo que esta respuesta cubriría la carga de la biblioteca al encontrar la ruta del directorio bin.

Primera solución

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

Segunda solución

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

Puede usar el mismo principio para las interfaces (estaría creando una clase pero enviando a la interfaz), como:

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

Este ejemplo es para la aplicación web, pero podría usarse similar para la aplicación de escritorio, solo la ruta se resuelve de manera diferente, por ejemplo

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
fuente
5

Es fácil.

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

Aquí hay un enlace de referencia

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

usuario3722131
fuente
Esa es una forma horrible de soportar la carga dinámica de código. A MS siempre le ha gustado obligarnos a entrar en demasiados detalles.
Más claro
3

A partir de Framework v4.5, puede usar Activator.CreateInstanceFrom () para instanciar clases fácilmente dentro de ensamblajes. El siguiente ejemplo muestra cómo usarlo y cómo llamar a un método pasando parámetros y obteniendo el valor de retorno.

    // 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
fuente
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
fuente
2

Puede cargar un ensamblaje utilizando los métodos * Assembly.Load **. Usando Activator.CreateInstance puede crear nuevas instancias del tipo que desee. Tenga en cuenta que debe usar el nombre de tipo completo de la clase que desea cargar (por ejemplo, Namespace.SubNamespace.ClassName ). Usando el método InvokeMember del Tipo clase puede invocar métodos en el tipo.

Además, tenga en cuenta que una vez cargado, un ensamblado no se puede descargar hasta que también se descargue todo el dominio de aplicación (esto es básicamente una pérdida de memoria).

Dario Solera
fuente
2

Dependiendo de cuán intrínseco sea este tipo de funcionalidad para su proyecto, es posible que desee considerar algo como MEF, que se encargará de cargar y unir los componentes por usted.

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

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

dynamic instanceOfMyType = Activator.CreateInstance(type);

De esta manera, puede usar funciones que no sean para obtener información sobre métodos y luego invocarla. Hará como esta instanciaOfMyType.MethodName (); Pero no puede usar Intellisense porque los tipos dinámicos se escriben en tiempo de ejecución, no en tiempo de compilación.

David Mkheyan
fuente
1

Sí, es conveniente utilizar el método de carga estática en la clase de ensamblaje, y luego llamar y luego llamar al método CreateInstance en la instancia de ensamblado que le devolvió la llamada a Load.

Además, puede llamar a uno de los otros métodos estáticos que comienzan con "Cargar" en la clase Ensamblaje, según sus necesidades.

casperOne
fuente
0

Puedes hacer esto de esta manera:

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
fuente