请在标记为重复,已经回答,暂停或偏离主题之前阅读。
首先,我知道这里有类似的问题,答案非常好。不幸的是他们没有回答我的问题。
我的目标:创建一个从特定接口继承的程序集中的所有类型的列表。该程序集可以位于本地路径或UNC路径上。
我的问题:首先,我知道如何从UNC和本地路径加载程序集,我知道如何使用GetTypes()。你们中的许多人可能知道GetTypes()有它自己的问题。具体来说,如果任何类型继承自另一个无法加载的类型,它将引发异常。例如How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes()。问题是,我不需要加载任何依赖项。我只需要查看程序集中声明的类型是否继承自其他特定类型。
一些代码作为例子。这只是检索程序集中类型的第一步:
Assembly asm = Assembly.Load(File.ReadAllBytes(assemblyPath));
Type[] myTypes = GetLoadableTypes(asm);
public static Type[] GetLoadableTypes(Assembly assembly)
{
// TODO: Argument validation
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
如果程序集内部的任何类型继承自无法加载的类型,则抛出ReflectionTypeLoadException异常,它们将在e.Types中显示为null,因此仍然没有关于它们的信息。所以我想我还是不能使用GetTypes。
为了完整起见,这里是对我原始问题的引用。 How to get Type information when ReflectionTypeLoadException is thrown from Assembly.GetType()
答案 0 :(得分:0)
好的,在这方面取得了一些进展,除非我想编写自己的元数据解析器,看起来这就是我所困扰的所以我想我会分享。我会尝试给出利弊。
这个答案都围绕着使用事件处理程序
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve
事件。应该指出的是,只有在解决程序集的依赖关系时才会引发此事件。我一开始并不明白,但它只是意味着事件不是为你试图加载的程序集触发的,而是该程序集中的任何依赖项,这是有道理的。当你必须首先有一个起点时,为什么要举起那个事件。对于我的解决方案,当我调用.GetTypes()时会引发此事件。
在我发布代码之前,让我解释一下这个想法以及它的优缺点。我的想法是,如果我有一个依赖项,那么将引发该事件。在这种情况下,我将首先检查在原始程序集中在同一文件夹(包括子文件夹)中找到的所有程序集之前创建的字典。如果在该字典中找到了args.Name,那么我读入该程序集并从事件中返回它。如果它不在列表中,我尝试仅按名称加载程序集,这意味着依赖程序集必须是项目的一部分或安装在本地GAC中。
我已经在这里暗示了一些缺点,所以让我们从这些开始。 因为我不只是查看元数据来告诉我主程序集中的依赖项(换句话说必须加载那些依赖项),如果无法在原始程序集中找到依赖项的程序集(在相同的文件夹或子文件夹中),并且依赖项未安装在本地GAC中,或者在项目中引用,GetTypes()仍将抛出ReflectionTypeLoadException异常。对于依赖程序集不在原始程序集中或安装在GAC中的情况而言,这似乎是一种罕见的情况,但我会将PRISM作为可能发生的情况的示例。 PRISMs程序集不一定安装到GAC中,并不总是在本地复制。
那么这种方法有什么好处呢?好吧,主要是它为你提供了至少一些方法来处理99%没有依赖程序集合的情况。
在代码之前的一两个最终想法(实际上是陷阱)。我使用AssemblyName创建一个基于程序集全名的字典,而不实际加载(直接或通过ReflectionOnlyLoad)程序集。有两种方法可以创建AssemblyName实例,可以通过构造函数传递给程序集的路径,也可以使用静态方法GetAssemblyName(path)。构造函数似乎只处理GetAssemblyName()可以处理UNC路径的本地路径。这些都是陷阱:不要仅仅为了获取名称而加载程序集,并确保不限制自己局限于本地路径。
最后,一些代码:
public class TestClass
{
//This dictionary will hold a list of the full path for an assembly, indexed by the assembly's full name
private Dictionary<string, string> _allAsms = new Dictionary<string, string>();
/// <summary>
/// Tries to list all of the Types inside an assembly and any interfaces those types inherit from.
/// </summary>
/// <param name="pathName">The path of the original assembly, without the assembly file</param>
/// <param name="fileName">The name of the assembly file as it is found on disk.</param>
public void Search(string pathName, string fileName)
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
//Getting a list of all possible assembly files that exist in the same location as the original assembly
string[] filePaths = Directory.GetFiles(pathName, "*.dll", SearchOption.AllDirectories);
Assembly asm;
AssemblyName name;
if (!pathName.EndsWith("\\"))
pathName += "\\";
foreach (string path in filePaths)
{
name = AssemblyName.GetAssemblyName(path);
if (!_allAsms.ContainsKey(name.FullName))
_allAsms.Add(name.FullName, path);
}
//This is where we are loading the originaly assembly
asm = System.Reflection.Assembly.ReflectionOnlyLoad(File.ReadAllBytes(pathName + fileName));
Console.WriteLine("Opened assembly:{0}", fileName);
//And this is where the ReflectionOnlyAssemblyResolve will start to be raised
foreach (Type t in asm.GetTypes())
{
Console.WriteLine(" " + t.FullName);
//Get the interfaces for the type;
foreach (Type dep in t.GetInterfaces())
{
Console.WriteLine(" " + dep.FullName);
}
}
}
private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
if (_allAsms.ContainsKey(args.Name))
return Assembly.ReflectionOnlyLoad(File.ReadAllBytes(_allAsms[args.Name]));
else
return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name);
}
}
答案 1 :(得分:-1)
要检查某个类型是否实现了特定的接口,您可以使用它:
typeof(yourInterface).IsAssignableFrom(type)
但是,如果无法加载类型,则没有可用的信息,您也无法确定它是否实现了特定的接口。