请阅读整个问题。我有一个独特的情况,有几个我想解决的限制。
在我的代码中,我有一个表达式树,它被编译为Predicate<System.Type>
. 我的目标是加载一个程序集而不锁定它(它是项目的输出程序集,不断重建),将此谓词应用于其类型列表并返回结果类型名称列表:
// this is what I need:
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName);
这个程序集应该加载到另一个 appdomain 中,因为我想在获得所需信息后立即卸载它。
这就是棘手的地方。我面临几个问题:
如果我在另一个 appdomain 中加载程序集并简单地返回其所有类型的数组,以便我可以将谓词应用回我的主 appdomain 中,只要将类型编组回我的主 appdomain,我就会得到一个FileNotFoundException
,说明未找到此程序集。这是有道理的,因为程序集只被加载到我创建的另一个 appdomain 中。将它也加载到主应用程序域中会破坏目的。
或者,如果我尝试将谓词传递到另一个应用程序域,将其应用到那里并取回一个字符串数组(完整类型名称),我会得到一个SerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly."
,因为谓词是一个动态方法(从表达式树编译) .
将它加载到主应用程序域中不会有任何这些问题,但是由于在不卸载整个应用程序域的情况下无法卸载已加载的程序集,因此一旦程序集发生更改(重建后),任何尝试加载具有相同名称的程序集会导致异常。
上下文:
我正在为 ReSharper 构建一个名为Agent Mulder的插件。该插件背后的想法是分析解决方案中的 DI/IoC 容器注册,并帮助 ReSharper 找出通过 DI 容器注册的类型的用法(您可以在此处观看有关其工作原理的简短视频)。
在大多数情况下,分析容器注册很简单——我只需要检测足够的信息就可以知道哪些具体类型受到影响。在这个温莎城堡Component.For<IFoo>().ImplementedBy<Foo>()
的例子中:结果类型是显而易见的,所以是AllTypes.FromThisAssembly().BasedOn<IFoo>()
- 给我足够的信息来猜测将受这条线影响的具体类型。但是,请考虑在 Castle Windsor 中进行的此注册:
container.Register(Classes
.FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking")))
.Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>)))
.WithServiceAllInterfaces());
(来源)
此处的信息取决于仅在运行时评估的谓词。
由于我所能做的就是静态分析这一点,因此我手上有 ReSharper 的 AST(在 ReSharper 中称为 PSI)表示Where
子句中的 lambda 表达式。我可以将此 AST 转换为 LINQ 表达式树,然后将其编译为委托。
我的想法是通过反射加载输出程序集(由FromAssembly*
描述符确定),并将此委托应用于程序集的类型以获得类型名称(我只需要名称)。每次组件更改时都必须重新评估这一点(我现在不关心性能)。
总之,除非有人可以推荐一种更好的方法来确定受谓词影响的类型,否则我想知道如何通过反射来做到这一点(不幸的是我没有考虑其他元数据阅读器,因为我必须以某种方式转换将 lambda 表达式 AST 转换为不同数据类型的谓词,我不知道是否存在 1 对 1 的转换)。
感谢您的阅读。此问题可用时将获得 500 点奖励。