正确加载程序集,查找类和调用Run()方法

Bud*_*Joe 79 .net c# 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 for a better way to do next 3 lines
        IRunnable r = assembly.CreateInstance("TestRunner");
        if (r == null) throw new Exception("broke");
        r.Run();

    }
}
Run Code Online (Sandbox Code Playgroud)

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

更新:威廉埃德蒙森 - 见评论

cdi*_*ins 71

使用AppDomain

首先将组件加载到自己的组件中更安全,更灵活AppDomain.

所以不是先前给出的答案:

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();
Run Code Online (Sandbox Code Playgroud)

我会建议以下内容(改编自相关问题的答案):

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();
Run Code Online (Sandbox Code Playgroud)

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

如果您希望动态加载和卸载程序集具有更大的灵活性和强大功能,那么您应该查看托管外接程序框架(即System.AddIn命名空间).有关详细信息,请参阅有关MSDN的加载项和可扩展性的这篇文章.

  • 我认为[`CreateInstanceFromAndUnwrap`](https://msdn.microsoft.com/en-us/library/3c4f1xde(v = vs.110).aspx)需要_AssemblyName_而不是路径; 你的意思是`CreateFrom(path,fullname).Unwrap()`?我也被[`MarshalByRefObject`要求]烧毁了(http://stackoverflow.com/a/5380317/1037948) (2认同)

Jef*_*nal 46

如果您无法访问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);
Run Code Online (Sandbox Code Playgroud)

如果您有权访问IRunnable接口类型,则可以将实例转换为该实例(而不是TestRunner在动态创建或加载的程序集中实现的类型,对吧?):

  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();
Run Code Online (Sandbox Code Playgroud)


Chr*_*ett 11

我在我的规则引擎中正在寻找你正在寻找的东西,它使用CS-Script来动态编译,加载和运行C#.它应该很容易翻译成你想要的东西,我会举一个例子.首先,代码(精简版):

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将采用类型为T的接口,将.cs文件编译为程序集,实例化给定类型的类,并将该实例化类与T接口对齐.基本上,您只需要确保实例化的类实现该接口.我使用属性来设置和访问所有内容,如下所示:

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; }
}
Run Code Online (Sandbox Code Playgroud)

对于您的示例,您想要调用Run(),因此我将创建一个定义Run()方法的接口,如下所示:

public interface ITestRunner
{
    void Run();
}
Run Code Online (Sandbox Code Playgroud)

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

public class TestRunner : ITestRunner
{
    public void Run()
    {
        // implementation goes here
    }
}
Run Code Online (Sandbox Code Playgroud)

将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; }
}
Run Code Online (Sandbox Code Playgroud)

然后,在任何你想要调用它的地方,你可以运行:

ITestRunner testRunner = TestHarnessInterface;

if (null != testRunner)
{
    testRunner.Run();
}
Run Code Online (Sandbox Code Playgroud)

它可能适用于插件系统,但我的代码原样仅限于加载和运行一个文件,因为我们的所有规则都在一个C#源文件中.我认为将它修改为只传入你想要运行的每个类型/源文件是非常容易的.您只需将代码从getter移动到采用这两个参数的方法中.

另外,使用您的IRunnable代替ITestRunner.


Wil*_*son 5

您将需要使用反射来获取"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();
    }
}
Run Code Online (Sandbox Code Playgroud)