Programa de consola de muestra.
class Program
{
static void Main(string[] args)
{
// ... code to build dll ... not written yet ...
Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
// don't know what or how to cast here
// looking for a better way to do next 3 lines
IRunnable r = assembly.CreateInstance("TestRunner");
if (r == null) throw new Exception("broke");
r.Run();
}
}
Quiero construir dinámicamente un ensamblado (.dll) y luego cargar el ensamblado, crear una instancia de una clase y llamar al método Run () de esa clase. ¿Debo intentar transmitir la clase TestRunner a algo? No estoy seguro de cómo los tipos en un ensamblado (código dinámico) sabrían acerca de mis tipos en mi (ensamblaje estático / aplicación de shell). ¿Es mejor usar solo unas pocas líneas de código de reflexión para llamar a Run () solo en un objeto? ¿Cómo debería verse ese código?
ACTUALIZACIÓN: William Edmondson - ver comentario
c#
.net
reflection
BuddyJoe
fuente
fuente
export
yimport
clases en ensamblajes separados que se derivan de una interfaz conocidaRespuestas:
Utilice un dominio de aplicación
Es más seguro y flexible cargar
AppDomain
primero el conjunto en sí mismo .Entonces, en lugar de la respuesta dada anteriormente :
var asm = Assembly.LoadFile(@"C:\myDll.dll"); var type = asm.GetType("TestRunner"); var runnable = Activator.CreateInstance(type) as IRunnable; if (runnable == null) throw new Exception("broke"); runnable.Run();
Sugeriría lo siguiente (adaptado de esta respuesta a una pregunta relacionada ):
var domain = AppDomain.CreateDomain("NewDomainName"); var t = typeof(TypeIWantToLoad); var runnable = domain.CreateInstanceFromAndUnwrap(@"C:\myDll.dll", t.Name) as IRunnable; if (runnable == null) throw new Exception("broke"); runnable.Run();
Ahora puede descargar el ensamblaje y tener diferentes configuraciones de seguridad.
Si desea aún más flexibilidad y potencia para la carga y descarga dinámica de ensamblados, debe mirar el marco de complementos administrados (es decir, el
System.AddIn
espacio de nombres). Para obtener más información, consulte este artículo sobre complementos y extensibilidad en MSDN .fuente
CreateInstanceFromAndUnwrap
requiere AssemblyName en lugar de una ruta; ¿te refieresCreateFrom(path, fullname).Unwrap()
? También me quemé por elMarshalByRefObject
requisitoCreateInstanceAndUnwrap(typeof(TypeIWantToLoad).Assembly.FullName, typeof(TypeIWantToLoad).FullName)
¿ Quizás ?Si no tiene acceso a la
TestRunner
información de tipo en el ensamblado de llamada (parece que no puede), puede llamar al método de esta manera:Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll"); Type type = assembly.GetType("TestRunner"); var obj = Activator.CreateInstance(type); // Alternately you could get the MethodInfo for the TestRunner.Run method type.InvokeMember("Run", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, null);
Si tiene acceso al
IRunnable
tipo de interfaz, puede convertir su instancia a eso (en lugar delTestRunner
tipo, que se implementa en el ensamblaje creado o cargado dinámicamente, ¿verdad?):Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll"); Type type = assembly.GetType("TestRunner"); IRunnable runnable = Activator.CreateInstance(type) as IRunnable; if (runnable == null) throw new Exception("broke"); runnable.Run();
fuente
Estoy haciendo exactamente lo que está buscando en mi motor de reglas, que usa CS-Script para compilar, cargar y ejecutar dinámicamente C #. Debería ser fácilmente traducible a lo que está buscando y le daré un ejemplo. Primero, el código (simplificado):
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using CSScriptLibrary; namespace RulesEngine { /// <summary> /// Make sure <typeparamref name="T"/> is an interface, not just any type of class. /// /// Should be enforced by the compiler, but just in case it's not, here's your warning. /// </summary> /// <typeparam name="T"></typeparam> public class RulesEngine<T> where T : class { public RulesEngine(string rulesScriptFileName, string classToInstantiate) : this() { if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName"); if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate"); if (!File.Exists(rulesScriptFileName)) { throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName); } RulesScriptFileName = rulesScriptFileName; ClassToInstantiate = classToInstantiate; LoadRules(); } public T @Interface; public string RulesScriptFileName { get; private set; } public string ClassToInstantiate { get; private set; } public DateTime RulesLastModified { get; private set; } private RulesEngine() { @Interface = null; } private void LoadRules() { if (!File.Exists(RulesScriptFileName)) { throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName); } FileInfo file = new FileInfo(RulesScriptFileName); DateTime lastModified = file.LastWriteTime; if (lastModified == RulesLastModified) { // No need to load the same rules twice. return; } string rulesScript = File.ReadAllText(RulesScriptFileName); Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true); @Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>(); RulesLastModified = lastModified; } } }
Esto tomará una interfaz de tipo T, compilará un archivo .cs en un ensamblado, instanciará una clase de un tipo dado y alineará esa clase instanciada con la interfaz T. Básicamente, solo debe asegurarse de que la clase instanciada implemente esa interfaz. Utilizo propiedades para configurar y acceder a todo, así:
private RulesEngine<IRulesEngine> rulesEngine; public RulesEngine<IRulesEngine> RulesEngine { get { if (null == rulesEngine) { string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs"); rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName); } return rulesEngine; } } public IRulesEngine RulesEngineInterface { get { return RulesEngine.Interface; } }
Para su ejemplo, desea llamar a Run (), por lo que crearía una interfaz que defina el método Run (), así:
public interface ITestRunner { void Run(); }
Luego crea una clase que lo implemente, así:
public class TestRunner : ITestRunner { public void Run() { // implementation goes here } }
Cambie el nombre de RulesEngine a algo como TestHarness y establezca sus propiedades:
private TestHarness<ITestRunner> testHarness; public TestHarness<ITestRunner> TestHarness { get { if (null == testHarness) { string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs"); testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName); } return testHarness; } } public ITestRunner TestHarnessInterface { get { return TestHarness.Interface; } }
Luego, en cualquier lugar que desee llamarlo, puede ejecutar:
ITestRunner testRunner = TestHarnessInterface; if (null != testRunner) { testRunner.Run(); }
Probablemente funcionaría muy bien para un sistema de complementos, pero mi código tal como está se limita a cargar y ejecutar un archivo, ya que todas nuestras reglas están en un archivo fuente de C #. Sin embargo, creo que sería bastante fácil modificarlo para pasar el archivo de tipo / fuente para cada uno que quisiera ejecutar. Solo tendría que mover el código del captador a un método que tomara esos dos parámetros.
Además, use su IRunnable en lugar de ITestRunner.
fuente
Deberá utilizar la reflexión para obtener el tipo "TestRunner". Utilice el método Assembly.GetType.
class Program { static void Main(string[] args) { Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll"); Type type = assembly.GetType("TestRunner"); var obj = (TestRunner)Activator.CreateInstance(type); obj.Run(); } }
fuente
MethodInfo
del tipo y llamasInvoke
? (Entendí que la pregunta original especificaba que la persona que llama no sabía nada sobre el tipo en cuestión)Cuando construya su conjunto, puede llamar
AssemblyBuilder.SetEntryPoint
y luego recuperarlo de laAssembly.EntryPoint
propiedad para invocarlo.Tenga en cuenta que querrá usar esta firma y tenga en cuenta que no es necesario que tenga un nombre
Main
:static void Run(string[] args)
fuente