Assembly.GetTypes()を呼び出すときにReflectionTypeLoadExceptionを防ぐ方法


97

次のようなコードを使用して、特定のインターフェイスを実装する型のアセンブリをスキャンしようとしています。

public List<Type> FindTypesImplementing<T>(string assemblyPath)
{
    var matchingTypes = new List<Type>();
    var asm = Assembly.LoadFrom(assemblyPath);
    foreach (var t in asm.GetTypes())
    {
        if (typeof(T).IsAssignableFrom(t))
            matchingTypes.Add(t);
    }
    return matchingTypes;
}

私の問題は、たとえば、現在利用できないアセンブリを参照する型がアセンブリに含まれている場合など、場合によってはをReflectionTypeLoadException呼び出すときにが発生するasm.GetTypes()ことです。

私の場合、問題を引き起こすタイプには興味がありません。私が探している型には、利用できないアセンブリは必要ありません。

問題は、例外を引き起こすタイプを何らかの方法でスキップ/無視することは可能ですか?それでもアセンブリに含まれる他のタイプを処理することは可能ですか?


1
それはあなたが探しているものよりはるかに多くの書き直しかもしれませんが、MEFはあなたに同様の機能を提供します。実装するインターフェースを指定する[Export]タグで各クラスをマークするだけです。次に、その時点で必要なインターフェースのみをインポートできます。
Dirk Dastardly、2011年

@Drew、コメントありがとうございます。MEFの使用を考えていましたが、別の安価な解決策があるかどうかを確認したいと思いました。
M4N 2011年

プラグインクラスファクトリに既知の名前を付けて、Activator.CreateInstance()を直接使用できるようにするだけの簡単な回避策です。それにもかかわらず、アセンブリの解決の問題が原因でこの例外が発生した場合は、後で同様に発生する可能性があります。
ハンスパッサント

1
@ハンス:完全に理解しているとは思いません。私がスキャンしているアセンブリには、特定のインターフェイスを実装する型がいくつも含まれている可能性があるため、既知の型は1つではありません。(また、1つだけでなく、複数のアセンブリをスキャンしています)
M4N '25年

2
私はほとんど同じコードと同じ問題を抱えています。そして、私が探索するアセンブリはによって与えられますAppDomain.CurrentDomain.GetAssemblies()、これは私のマシンでは機能しますが、他のマシンでは機能しません。どうして私の実行可能ファイルの一部のアセンブリがとにかく読み取り/ロードできないのですか?
v.oddou 2014

回答:


130

かなり厄介な方法の1つは次のとおりです。

Type[] types;
try
{
    types = asm.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
    types = e.Types;
}
foreach (var t in types.Where(t => t != null))
{
    ...
}

しかし、これをしなければならないのは間違いなく迷惑です。拡張メソッドを使用して、「クライアント」コードでより良いものにすることができます。

public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
    // TODO: Argument validation
    try
    {
        return assembly.GetTypes();
    }
    catch (ReflectionTypeLoadException e)
    {
        return e.Types.Where(t => t != null);
    }
}

あなたも移動したいと思うかもしれreturncatchブロックの外に声明を-私はひどく、それが自分自身であることに熱心ではないんだけど、それはおそらくです最短のコード...


2
ありがとう、それは解決策のようです(そして私は同意します、それはクリーンな解決策のようではありません)。
M4N 2011年

4
例外で公開されているタイプのリストを使用しようとすると、このソリューションにはまだ問題があります。タイプロード例外の理由が何であれ、FileNotFound、BadImageなどは、問題のあるタイプへのすべてのアクセスをスローします。
sweetfa

@sweetfa:はい、それは非常に制限されています-たとえば、OPが名前を見つけるだけでよい場合は、大丈夫です。
Jon Skeet、2012年

1
おかしい、この投稿はここで引用されており、非常に興味深いです。haacked.com
2012/07/23

@sweetfaこれは、返された型でのFileNotFound例外の問題を回避するために私が行うFrom t As Type In e.Types Where (t IsNot Nothing) AndAlso (t.TypeInitializer IsNot Nothing)ことです。
ElektroStudios 2016

22

ある時点で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;
    }

4

Assembly.ReflectionOnlyLoadを検討しましたか?あなたがやろうとしていることを考えると、それで十分かもしれません。


2
はい、私はそれを考えていました。しかし、他の方法で依存関係を手動でロードする必要があるため、私はそれを使用しませんでした。また、コードはReflectionOnlyLoadでは実行できません(リンクしたページの「解説」セクションを参照してください)。
M4N 2011年

3

私の場合、アプリケーションフォルダーに不要なアセンブリが存在することで同じ問題が発生しました。Binフォルダをクリアして、アプリケーションを再構築してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.