Estoy tratando de averiguar cómo podría importar y usar un .dll en tiempo de ejecución dentro de una aplicación C #. Usando Assembly.LoadFile () he logrado que mi programa cargue el dll (esta parte definitivamente está funcionando ya que puedo obtener el nombre de la clase con ToString ()), sin embargo, no puedo usar la 'Salida' método desde el interior de mi aplicación de consola. Estoy compilando el .dll y luego lo muevo al proyecto de mi consola. ¿Hay un paso adicional entre CreateInstance y luego poder usar los métodos?
Esta es la clase en mi DLL:
namespace DLL
{
using System;
public class Class1
{
public void Output(string s)
{
Console.WriteLine(s);
}
}
}
y aquí está la aplicación que quiero cargar la 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#
reflection
dll
danbroooks
fuente
fuente
Respuestas:
Los miembros deben poder resolverse en tiempo de compilación para ser llamados directamente desde C #. De lo contrario, debe utilizar reflexión u objetos dinámicos.
Reflexión
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(); } } }
Dinámico (.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(); } } }
fuente
Output
a todos los tipos en el ensamblado, que probablemente se lanzarán antes de que se encuentre la clase "correcta" ...IDog dog = someInstance as IDog;
y probar si no es nula. Coloque sus interfaces en una DLL común compartida por los clientes, y cualquier complemento que se cargue dinámicamente debe implementar esa interfaz. Esto le permitirá codificar su cliente contra la interfaz IDog y tener intellisense + verificación de tipo fuerte en tiempo de compilación en lugar de usar dinámica.Class1
. En ese momento, puede usarnew Class1()
. El autor de la pregunta especificó explícitamente una dependencia de tiempo de ejecución.dynamic
permite que el programa no requiera una dependencia en tiempo de compilación deClass1
.En este momento, está creando una instancia de cada tipo definido en el ensamblaje . Solo necesita crear una única instancia de
Class1
para llamar al método: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(); } }
fuente
Debe crear una instancia del tipo que exponga el
Output
método: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(); }
fuente
Activator.CreateInstance()
devuelve un objeto, que no tiene un método de salida.¿Parece que vienes de lenguajes de programación dinámicos? C # definitivamente no es eso, y lo que está tratando de hacer será difícil.
Dado que está cargando un dll específico desde una ubicación específica, ¿tal vez solo desee agregarlo como referencia a su aplicación de consola?
Si absolutamente desea cargar el ensamblaje a través de
Assembly.Load
, tendrá que ir a través de la reflexión para llamar a los miembros enc
Algo como
type.GetMethod("Output").Invoke(c, null);
debería hacerlo.fuente
foreach (var f in Directory.GetFiles(".", "*.dll")) Assembly.LoadFrom(f);
Eso carga todas las DLL presentes en la carpeta de su ejecutable.
En mi caso, estaba tratando de usar
Reflection
para encontrar todas las subclases de una clase, incluso en otras DLL. Esto funcionó, pero no estoy seguro de si es la mejor manera de hacerlo.EDITAR: Lo cronometré, y solo parece cargarlos la primera vez.
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); }
Salida: 34 0 0 0
Por lo tanto, uno podría ejecutar ese código antes de cualquier búsqueda de Reflection por si acaso.
fuente
No es tan dificil.
Puede inspeccionar las funciones disponibles del objeto cargado y, si encuentra la que está buscando por su nombre, busque sus parámetros esperados, si los hay. Si es la llamada que está tratando de encontrar, llámela usando el método Invoke del objeto MethodInfo.
Otra opción es simplemente construir sus objetos externos en una interfaz y convertir el objeto cargado en esa interfaz. Si tiene éxito, llame a la función de forma nativa.
Esto es algo bastante simple.
fuente