C# 加载程序集、查找类和调用Run()方法的正确方法

C# 加载程序集、查找类和调用Run()方法的正确方法,c#,.net,reflection,C#,.net,Reflection,示例控制台程序 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

示例控制台程序

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

    }
}
我想动态构建一个程序集(.dll),然后加载该程序集,实例化一个类,并调用该类的Run()方法。我是否应该尝试将TestRunner类转换为其他类型?不确定一个程序集中的类型(动态代码)如何知道my(静态程序集/shell应用程序)中的类型。仅仅使用几行反射代码来调用一个对象上的Run()是否更好?代码应该是什么样子

更新:
William Edmondson-请参见注释

您需要使用反射来获取类型“TestRunner”。使用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();
    }
}

如果您没有访问调用程序集中的
TestRunner
类型信息的权限(听起来可能没有),您可以这样调用该方法:

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);
如果您有权访问
IRunnable
接口类型,则可以将实例强制转换为该类型(而不是
TestRunner
类型,它是在动态创建或加载的程序集中实现的,对吗?):


构建程序集时,您可以调用,然后从属性获取它以调用它

请记住,您将希望使用此签名,并注意它不必命名为
Main

static void Run(string[] args)

我的规则引擎用于动态编译、加载和运行C#,这正是您所希望的。它应该很容易翻译成你想要的,我会举个例子。首先,代码(精简):

然后创建一个实现它的类,如下所示:

public interface ITestRunner
{
    void Run();
}
public class TestRunner : ITestRunner
{
    public void Run()
    {
        // implementation goes here
    }
}
将RulesEngine的名称更改为类似TestHarness的名称,并设置属性:

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; }
}
对于插件系统来说,这可能非常有用,但我的代码仅限于加载和运行一个文件,因为我们所有的规则都在一个C源文件中。不过,我认为修改它以只为每个要运行的文件传入类型/源文件是非常容易的。您只需将代码从getter移动到一个接受这两个参数的方法中

另外,使用IRunnable代替ITestRunner。

使用AppDomain 首先将部件加载到自己的部件中更安全、更灵活

因此,不是:

我建议如下(改编自):

现在,您可以卸载程序集并具有不同的安全设置


如果您想在动态加载和卸载程序集方面获得更大的灵活性和功能,那么应该查看托管加载项框架(即
System.AddIn
命名空间)。有关更多信息,请参阅上的这篇文章。

这是否缺少一个步骤,在该步骤中,您可以从类型和调用
Invoke
中获得相应的
MethodInfo
?(我把最初的问题理解为指定调用方对所讨论的类型一无所知。)您遗漏了一件事。您必须强制转换obj以键入TestRunner。var obj=(TestRunner)Activator.CreateInstance(类型);听起来Tyndall实际上是在前面的步骤中构建这个dll。这个实现假设他知道Run()方法已经存在,并且知道它没有参数。如果这些确实是未知的,那么他需要做一些更深入的思考。TestRunner是动态编写代码中的一个类。因此,示例中的静态代码无法解析TestRunner。它不知道它是什么。@WilliamEdmondson在代码中如何使用“(TestRunner)”,因为这里没有引用它?什么是AssemblyBuilder?我尝试了CodeDomProvider,然后是“provider.CompileAssemblyFromSource”+1,它使用type.invokeMember行工作。我应该使用这种方法还是继续尝试对接口做些什么?我甚至不必担心将其放入动态构建的代码中。嗯,第二个代码块对您不起作用吗?调用程序集是否有权访问iRunTable类型?第二个块不起作用。调用程序集并不真正了解IRunnable。所以我想我会坚持第二种方法。轻微跟进。当我重新生成代码,然后重做dyn.dll时,我似乎无法替换它,因为它正在使用中。类似Assembly.UnloadType或允许我替换.dll的东西?或者我应该“在内存中”这样做?思想?谢谢,如果“内存中”是最好的解决方案,我不知道该怎么做。我记不起具体细节(我要离开我的计算机一段时间),但我相信每个AppDomain只能加载一次程序集-因此,您必须为每个程序集实例创建新的AppDomain(并将程序集加载到这些程序中)或者,在编译新版本的程序集之前,您必须重新启动应用程序。@接口是什么?这里的想法很酷。需要充分消化。+1非常有趣,我没有意识到C#解析器必须查看一个字符,然后通过@来查看它是变量名的一部分还是@“”string.Thank。当变量名是关键字时,会使用变量名前的@。您不能将变量命名为“class”、“interface”、“new”等。但如果您在@前加上@,则可以。在我的情况下,大写字母“I”可能无关紧要,但在我将其转换为自动属性之前,它最初是一个带有getter和setter的内部变量。没错。我忘记了@thing。你如何处理我向Jeff Sternal提出的关于“内存中的东西”的问题?我想我现在最大的问题是我可以构建dynamic.dll并加载它,但我只能做一次。不知道如何“卸载”程序集。是否可以创建另一个AppDomain在该空间中加载程序集,使用它,然后取下第二个AppDomain。冲洗。重复。?除非使用第二个AppDomain,否则无法卸载程序集。我不确定CS脚本在内部是如何完成的,但我剥离的规则引擎部分是一个FileSystemWatch当文件更改时,它会自动再次运行LoadRules()。我们编辑规则,将它们推送到
public interface ITestRunner
{
    void Run();
}
public class TestRunner : ITestRunner
{
    public void Run()
    {
        // implementation goes here
    }
}
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; }
}
ITestRunner testRunner = TestHarnessInterface;

if (null != testRunner)
{
    testRunner.Run();
}
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();
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();