4

我正在尝试将程序集加载到另一个应用程序域(以便能够在不需要时卸载它们),但我也希望能够检查加载的程序集中的类型并创建我的类型的实例(通过 Activator)。此外,我不希望程序集在加载时被锁定。

我不明白这怎么可能。我尝试过的事情:

  • Loading Assembly.Load(File.ReadAllBytes(path)) 帮助我加载程序集而不锁定它,因此它可以被删除/移动。这会将程序集加载到当前的 appdomain 中,因此无法卸载它。

  • 创建另一个 AppDomain 并使用 AppDomain.Load() 将程序集加载到新的 AppDomain 中,但无法检查加载的 AppDomain 中的所有类型。除非我知道类型完全限定的类型名称,否则我无法创建任何东西,即使那样它们也必须是可序列化的或从 MarshallByRef 派生的。另一个 AppDomain 的东西只能通过代理工作,所以如果没有合同/共享接口等,就很难创建东西。

  • MEF 还将程序集加载到当前的 AppDomain 中,因此它基本上是在做同样的事情。

  • Mono.Cecil 允许对加载的程序集进行类型检查,但是我无法使用 TypeReference 创建类型。也许如果有办法将 TypeReference 转换为 Type?

我检查了 ILSpy 是如何做到这一点的,毫不奇怪,它使用 Mono.Cecil 加载/卸载程序集,但话又说回来,它不会创建任何类型的实例,只是进行可能通过 Mono.Cecil 进行的类型检查路线。

现在的问题是,这甚至可能吗?我错过了什么吗?

4

1 回答 1

1

如果您将程序集加载到另一个应用程序域,那么您当然应该在那里创建实例并在那里使用它们。除此之外,不确定您遇到了什么问题。

更新

  1. 添加代码以从列表中选择类型
  2. 更改为仅将跨域的类型名称作为字符串传递

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;

public class MainClass : MarshalByRefObject
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("usage: {0} assembly", Path.GetFileName(Assembly.GetExecutingAssembly().Location));
            return;
        }
        AppDomain other = AppDomain.CreateDomain("other");
        Type myType = typeof(MainClass);
        // create a loader instance in the new domain
        MainClass loader = (MainClass)other.CreateInstanceAndUnwrap(myType.Assembly.FullName, myType.FullName);
        string assemblyName;
        string[] types = loader.LoadAssembly(args[0], out assemblyName);
        Console.WriteLine("Loaded assembly {0}.", assemblyName);
        Console.WriteLine("Types:");
        for(int i = 0; i < types.Length; i += 1)
        {
            Console.WriteLine("[{0}] {1}", i, types[i]);
        }
        Console.Write("Enter index of type to create, -1 to exit: ");
        int create = Int32.Parse(Console.ReadLine());
        if (create < 0)
        {
            return;
        }
        Console.WriteLine("Creating instance of type {0}", types[create]);
        Console.WriteLine("Type of the created instance was: {0}", loader.CreateInstance(assemblyName, types[create]));
    }

    string[] LoadAssembly(string path, out string assemblyName)
    {
        Console.WriteLine("LoadAssembly executing in appdomain {0}", AppDomain.CurrentDomain.FriendlyName);
        Assembly assembly = Assembly.Load(File.ReadAllBytes(path));
        assemblyName = assembly.FullName;
        return Array.ConvertAll<Type, string>(assembly.GetExportedTypes(), x => x.FullName);
    }

    string CreateInstance(string assemblyName, string typeName)
    {
        Console.WriteLine("CreateInstance executing in appdomain {0}", AppDomain.CurrentDomain.FriendlyName);
        object instance = Activator.CreateInstance(assemblyName, typeName).Unwrap();
        // do something useful with the instance here
        return instance.GetType().FullName;
    }

    public override object InitializeLifetimeService()
    {
        return null;
    }
}

示例输出log4net.dll

$ mono main.exe log4net.dll
LoadAssembly executing in appdomain other
Loaded assembly log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=a5715cc6d5c3540b.
Types:
[0] log4net.Core.SecurityContext
[1] log4net.Core.LoggerWrapperImpl
[2] log4net.Core.LogImpl
[3] log4net.Core.DefaultRepositorySelector
...
[160] log4net.Appender.AspNetTraceAppender
[161] log4net.Appender.FileAppender
...
[197] log4net.Repository.LoggerRepositoryConfigurationResetEventHandler
[198] log4net.Repository.LoggerRepositoryConfigurationChangedEventHandler
Enter index of type to create, -1 to exit: 161
Creating instance of type log4net.Appender.FileAppender
CreateInstance executing in appdomain other
Type of the created instance was: log4net.Appender.FileAppender
于 2012-12-03T21:24:12.187 回答