.net 调用Assembly.GetTypes()时如何防止ReflectionTypeLoadException

.net 调用Assembly.GetTypes()时如何防止ReflectionTypeLoadException,.net,reflection,plugins,.net-assembly,.net,Reflection,Plugins,.net Assembly,我正在尝试使用类似以下代码扫描程序集,查找实现特定接口的类型: 公共列表查找类型简化(字符串汇编路径) { var matchingTypes=新列表(); var asm=Assembly.LoadFrom(assemblyPath); foreach(asm.GetTypes()中的var t) { if(类型(T).IsAssignableFrom(T)) 匹配类型。添加(t); } 返回匹配类型; } 我的问题是,在某些情况下(例如,如果程序集包含引用当前不可用的程序集的类型),调用a

我正在尝试使用类似以下代码扫描程序集,查找实现特定接口的类型:

公共列表查找类型简化(字符串汇编路径)
{
var matchingTypes=新列表();
var asm=Assembly.LoadFrom(assemblyPath);
foreach(asm.GetTypes()中的var t)
{
if(类型(T).IsAssignableFrom(T))
匹配类型。添加(t);
}
返回匹配类型;
}
我的问题是,在某些情况下(例如,如果程序集包含引用当前不可用的程序集的类型),调用
asm.GetTypes()
时,我会得到一个
ReflectionTypeLoadException

就我而言,我对导致问题的类型不感兴趣。我正在搜索的类型不需要不可用的程序集


问题是:是否有可能跳过/忽略导致异常的类型,但仍处理程序集中包含的其他类型?

一种相当糟糕的方法是:

Type[] types;
try
{
    types = asm.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
    types = e.Types;
}
foreach (var t in types.Where(t => t != null))
{
    ...
}
但是不得不这样做确实很烦人。您可以使用扩展方法在“客户机”代码中使其更好:

公共静态IEnumerable GetLoadableTypes(此程序集)
{
//TODO:参数验证
尝试
{
返回assembly.GetTypes();
}
捕获(ReflectionTypeLoadException e)
{
返回e.Types.Where(t=>t!=null);
}
}

您可能希望将
return
语句从catch块中移出-我自己并不太喜欢它,但它可能是最短的代码…

您考虑过吗?考虑到您正在尝试做的事情,这可能就足够了。

虽然在某个时候如果不接收ReflectionTypeLoadException,似乎什么都做不了,上述答案是有限的,因为任何试图使用例外情况下提供的类型的尝试仍然会与导致该类型无法加载的原始问题产生分歧

为了克服这个问题,下面的代码将类型限制为位于程序集中的类型,并允许谓词进一步限制类型列表

    /// <summary>
    /// Get the types within the assembly that match the predicate.
    /// <para>for example, to get all types within a namespace</para>
    /// <para>    typeof(SomeClassInAssemblyYouWant).Assembly.GetMatchingTypesInAssembly(item => "MyNamespace".Equals(item.Namespace))</para>
    /// </summary>
    /// <param name="assembly">The assembly to search</param>
    /// <param name="predicate">The predicate query to match against</param>
    /// <returns>The collection of types within the assembly that match the predicate</returns>
    public static ICollection<Type> GetMatchingTypesInAssembly(this Assembly assembly, Predicate<Type> predicate)
    {
        ICollection<Type> types = new List<Type>();
        try
        {
            types = assembly.GetTypes().Where(i => i != null && predicate(i) && i.Assembly == assembly).ToList();
        }
        catch (ReflectionTypeLoadException ex)
        {
            foreach (Type theType in ex.Types)
            {
                try
                {
                    if (theType != null && predicate(theType) && theType.Assembly == assembly)
                        types.Add(theType);
                }
                // This exception list is not exhaustive, modify to suit any reasons
                // you find for failure to parse a single assembly
                catch (BadImageFormatException)
                {
                    // Type not in this assembly - reference to elsewhere ignored
                }
            }
        }
        return types;
    }
//
///获取程序集中与谓词匹配的类型。
///例如,获取命名空间中的所有类型
///typeof(SomeClassInAssemblyYouWant).Assembly.GetMatchingTypesInAssembly(item=>“MyNamespace”.Equals(item.Namespace))
/// 
///要搜索的程序集
///要匹配的谓词查询
///程序集中与谓词匹配的类型集合
公共静态ICollection GetMatchingTypesInAssembly(此程序集,谓词)
{
i收集类型=新列表();
尝试
{
types=assembly.GetTypes().Where(i=>i!=null&&predicate(i)&&i.assembly==assembly.ToList();
}
捕获(ReflectionTypeLoadException ex)
{
foreach(在ex.Types中键入类型)
{
尝试
{
if(theType!=null&&predicate(theType)&&theType.Assembly==Assembly)
类型。添加(类型);
}
//此例外列表并非详尽无遗,请根据任何原因进行修改
//您发现解析单个程序集失败
捕获(BadImageFormatException)
{
//类型不在此程序集中-忽略对其他位置的引用
}
}
}
返回类型;
}

在我的例子中,同样的问题是由应用程序文件夹中存在不需要的程序集引起的。尝试清除Bin文件夹并重建应用程序。

Jon Skeet的回答很好,但每次都会遇到这个异常。要解决这个问题,请使用以下代码段并在VisualStudio的调试设置中启用“仅我的代码”

[DebuggerNonUserCode]
公共静态IEnumerable GetSuccessfullyLoadedTypes(程序集)
{
尝试
{
返回assembly.GetTypes();
}
捕获(ReflectionTypeLoadException e)
{
//由于未知的原因,有时无法加载某些类型并引发此异常。
//[DebuggerNonUserCode]确保开发人员不会抛出这些异常
//当他在调试设置中打开“仅我的代码”时面对。
返回e.Types.Where(t=>t!=null);
}
}

这可能比您要寻找的内容更需要重写,但MEF为您提供了类似的功能。只需使用[Export]标记每个类,该标记指定它实现的接口。然后您只能导入您当时感兴趣的接口。@Drew,谢谢您的评论。我正在考虑使用MEF,但想看看是否有另一个更便宜的解决方案。给插件类工厂一个众所周知的名称,这样你就可以直接使用Activator.CreateInstance()了,这是一个简单的解决方法。不过,如果您现在因为程序集解析问题而得到此异常,那么您以后可能也会得到它。@Hans:我不确定我是否完全理解。我正在扫描的程序集可能包含任意数量的实现给定接口的类型,因此没有一个已知类型。(而且:我扫描了不止一个程序集,而不仅仅是一个)我有几乎相同的代码和相同的问题。我探索的程序集由AppDomain.CurrentDomain.GetAssemblies()给出,这在我的机器上有效,但在其他机器上无效。为什么我的可执行文件中的某些程序集无论如何都无法读取/加载?谢谢,这似乎是一个解决方案(我同意,这似乎不是一个干净的解决方案)。当您尝试使用异常中公开的类型列表时,此解决方案仍然存在问题。无论类型加载异常的原因是什么,FileNotFound、BadImage等都会导致
    /// <summary>
    /// Get the types within the assembly that match the predicate.
    /// <para>for example, to get all types within a namespace</para>
    /// <para>    typeof(SomeClassInAssemblyYouWant).Assembly.GetMatchingTypesInAssembly(item => "MyNamespace".Equals(item.Namespace))</para>
    /// </summary>
    /// <param name="assembly">The assembly to search</param>
    /// <param name="predicate">The predicate query to match against</param>
    /// <returns>The collection of types within the assembly that match the predicate</returns>
    public static ICollection<Type> GetMatchingTypesInAssembly(this Assembly assembly, Predicate<Type> predicate)
    {
        ICollection<Type> types = new List<Type>();
        try
        {
            types = assembly.GetTypes().Where(i => i != null && predicate(i) && i.Assembly == assembly).ToList();
        }
        catch (ReflectionTypeLoadException ex)
        {
            foreach (Type theType in ex.Types)
            {
                try
                {
                    if (theType != null && predicate(theType) && theType.Assembly == assembly)
                        types.Add(theType);
                }
                // This exception list is not exhaustive, modify to suit any reasons
                // you find for failure to parse a single assembly
                catch (BadImageFormatException)
                {
                    // Type not in this assembly - reference to elsewhere ignored
                }
            }
        }
        return types;
    }