获取自定义程序集属性而不加载到当前AppDomain中

我创建了一个小应用程序来递归加载提供的目录中的程序集并读取它们的自定义属性集合。 主要是阅读DebuggableAttribute来确定IsJITTrackingEnabled和IsJITOptimizerDisabled的设置,以确定程序集是否针对发布进行了优化。

我当前的代码执行Assembly.LoadFrom以传递到程序集的整个路径并加载它。 然后在程序集上执行GetCustomAttributes以获取debuggable属性。 问题是每个程序集都加载到当前的appdomain中。 因此,如果另一个文件夹使用相同的程序集,它只使用最初加载的引用。 我希望能够加载程序集,读取我需要的属性,然后卸载它。 我尝试创建一个新的appdomain并将程序集加载到其中然后卸载程序集后跟无济于事。

我知道这一定是可能的,但我不知所措。 任何帮助将不胜感激。 我很乐意提供您可能需要的任何其他信息。

简短的回答是,不,没有办法做你所要求的。

更长的答案是:有一个特殊的程序集加载方法, Assembly.ReflectionOnlyLoad() ,它使用“仅reflection”加载上下文。 这使您可以加载无法执行但可以读取其元数据的程序集。

在你的情况下(显然,在每个用例中我都可以自己提出来)它并没有那么有用。 您无法从此类程序CustomAttributeData获取类型化属性,只能获取CustomAttributeData 。 该类没有提供任何过滤特定属性的好方法(我能想到的最好的方法是将它转换为字符串并使用StartsWith("[System.Diagnostics.Debuggable");

更糟糕的是,仅reflection加载不会加载任何依赖项程序集,但它会强制您手动执行 。 这使得它客观上比你现在所做的更糟糕; 至少现在你自动获得依赖加载。

(另外,我之前的回答提到了MEF;我错了,看来MEF包含了大量的自定义reflection代码来实现这一function。)

最终,装载后无法卸载assembly。 您需要卸载整个应用程序域 ,如本MSDN文章中所述。

更新:

正如评论中所指出的,我能够通过仅reflection加载(以及正常加载)获取所需的属性信息,但缺少类型化属性元数据会使其变得非常痛苦。

如果加载到正常的汇编上下文中,您可以轻松获得所需的信息:

 var d = a.GetCustomAttributes(typeof(DebuggableAttribute), false) as DebuggableAttribute; var tracking = d.IsJITTrackingEnabled; var optimized = !d.IsJITOptimizerDisabled; 

如果加载到仅reflection的上下文中,您可以做一些工作; 你必须弄清楚属性构造函数采用的forms,知道默认值是什么,并结合这些信息来得出每个属性的最终值。 您可以获得所需的信息,如下所示:

 var d2 = a.GetCustomAttributesData() .SingleOrDefault(x => x.ToString() .StartsWith("[System.Diagnostics.DebuggableAttribute")); 

从那里,您需要检查ConstructorArguments以查看调用了哪个构造函数: 这个带有一个参数或带有两个参数的构造函数。 然后,您可以使用适当参数的值来确定您感兴趣的两个属性将采用的值:

 if (d2.ConstructorArguments.Count == 1) { var mode = d2.ConstructorArguments[0].Value as DebuggableAttribute.DebuggingModes; // Parse the modes enumeration and figure out the values. } else { var tracking = (bool)d2.ConstructorArguments[0].Value; var optimized = !((bool)d2.ConstructorArguments[1].Value); } 

最后,您需要检查可能覆盖构造函数上设置的NamedArguments ,例如:

 var arg = NamedArguments.SingleOrDefault(x => x.MemberInfo.Name.Equals("IsJITOptimizerDisabled")); var optimized = (arg == null || !((bool)arg.TypedValue.Value)); 

最后要注意的是,如果您在.NET 2.0或更高版本下运行它,并且还没有看到,MSDN会在DebuggingModes文档中指出这一点:

在.NET Framework 2.0版中,始终生成JIT跟踪信息,并且此标志与Default具有相同的效果,但IsJITTrackingEnabled属性为false,但在2.0版中没有任何意义。

您需要使用Assembly.ReflectionOnlyLoad

以下是一些MSDN Notes ,展示了如何使用它:

 using System; using System.IO; using System.Reflection; public class ReflectionOnlyLoadTest { public ReflectionOnlyLoadTest(String rootAssembly) { m_rootAssembly = rootAssembly; } public static void Main(String[] args) { if (args.Length != 1) { Console.WriteLine("Usage: Test assemblyPath"); return; } try { ReflectionOnlyLoadTest rolt = new ReflectionOnlyLoadTest(args[0]); rolt.Run(); } catch (Exception e) { Console.WriteLine("Exception: {0}!!!", e.Message); } } internal void Run() { AppDomain curDomain = AppDomain.CurrentDomain; curDomain.ReflectionOnlyPreBindAssemblyResolve += new ResolveEventHandler(MyReflectionOnlyResolveEventHandler); Assembly asm = Assembly.ReflectionOnlyLoadFrom(m_rootAssembly); // force loading all the dependencies Type[] types = asm.GetTypes(); // show reflection only assemblies in current appdomain Console.WriteLine("------------- Inspection Context --------------"); foreach (Assembly a in curDomain.ReflectionOnlyGetAssemblies()) { Console.WriteLine("Assembly Location: {0}", a.Location); Console.WriteLine("Assembly Name: {0}", a.FullName); Console.WriteLine(); } } private Assembly MyReflectionOnlyResolveEventHandler(object sender, ResolveEventArgs args) { AssemblyName name = new AssemblyName(args.Name); String asmToCheck = Path.GetDirectoryName(m_rootAssembly) + "\\" + name.Name + ".dll"; if (File.Exists(asmToCheck)) { return Assembly.ReflectionOnlyLoadFrom(asmToCheck); } return Assembly.ReflectionOnlyLoad(args.Name); } private String m_rootAssembly; } 

在当前的AppDomain中卸载程序集是不可能的,这就是.NET设计不幸的方式。 这甚至是ReflectionOnly加载的情况。 这样做还有一点点皱纹,然后您需要使用GetCustomAttributesData方法而不是普通的GetCustomAttributes,因为后者需要在属性构造函数中运行代码。 这会让生活变得更加困难。

一个应该工作的替代方案是使用Cecil ,它允许您检查组件而不是在正常意义上实际加载它。 但这是一项额外的工作。

我相信Assembly.ReflectionOnlyLoad就是你要找的。