Assembly.GetTypes () çağrılırken ReflectionTypeLoadException nasıl engellenir


99

Buna benzer bir kod kullanarak belirli bir arabirim uygulayan türler için bir derlemeyi taramaya çalışıyorum:

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;
}

Benim sorunum, bazı durumlarda ReflectionTypeLoadExceptionçağırırken bir almam asm.GetTypes(), örneğin derleme şu anda mevcut olmayan bir derlemeye başvuran türler içeriyorsa.

Benim durumumda, soruna neden olan türlerle ilgilenmiyorum. Aradığım tipler, mevcut olmayan montajlara ihtiyaç duymuyor.

Soru şudur: istisnaya neden olan türleri bir şekilde atlamak / yok saymak, ancak yine de derlemede bulunan diğer türleri işlemek mümkün müdür?


1
Aradığınız şeyden çok daha fazla yeniden yazma olabilir, ancak MEF size benzer işlevsellik sağlar. Sınıflarınızın her birini, uyguladığı arayüzü belirten bir [Dışa Aktar] etiketiyle işaretlemeniz yeterlidir. Daha sonra, yalnızca o anda ilgilendiğiniz arabirimleri içe aktarabilirsiniz.
Dirk Dastardly

@Drew, Yorumunuz için teşekkürler. MEF'i kullanmayı düşünüyordum, ancak başka, daha ucuz bir çözüm olup olmadığını görmek istedim.
M4N

Activator.CreateInstance () 'ı doğrudan kullanabilmeniz için eklenti sınıfı fabrikasına iyi bilinen bir ad vermek basit bir çözümdür. Bununla birlikte, bu istisnayı bir montaj çözümü sorunu nedeniyle şimdi alırsanız, muhtemelen daha sonra da alırsınız.
Hans Passant

1
@Hans: Tamamen anladığımdan emin değilim. Taradığım derleme, verilen arabirimi uygulayan herhangi bir sayıda tür içerebilir, bu nedenle iyi bilinen bir tür yoktur. (ve ayrıca: Yalnızca bir değil birden fazla derleme
tarıyorum

2
Neredeyse aynı koda sahibim ve aynı sorun. Ve araştırdığım montaj AppDomain.CurrentDomain.GetAssemblies(), bu benim makinemde çalışıyor ama diğer makinelerde değil. Çalıştırılabilir dosyamdaki bazı derlemeler neden zaten okunamıyor / yüklenemiyor?
v.oddou

Yanıtlar:


130

Oldukça çirkin bir yol şu olabilir:

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

Bunu yapmak zorunda olmak kesinlikle can sıkıcı. "Müşteri" kodunda daha güzel hale getirmek için bir uzantı yöntemi kullanabilirsiniz:

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);
    }
}

İyi taşımak isteyebilirsiniz returnFeci orada kendim olmaya düşkün değilim, ama muhtemelen - catch bloğunun dışına deyimi olan en kısa kodu ...


2
Teşekkürler, bu bir çözüm gibi görünüyor (ve katılıyorum, temiz bir çözüm gibi görünmüyor).
M4N

4
İstisnada açığa çıkan türlerin listesini kullanmaya çalıştığınızda bu çözüm hala sorun yaşıyor. Tür yükleme istisnasının nedeni ne olursa olsun, FileNotFound, BadImage, vb. Yine de sorunlu türlere her erişime neden olacaktır.
sweetfa

@sweetfa: Evet, çok sınırlı - ancak örneğin OP'nin sadece isimleri bulması gerekiyorsa, sorun olmaz.
Jon Skeet

1
Komik, bu gönderi burada alıntı yapılmış, oldukça ilginç: haacked.com/archive/2012/07/23/…
anhoppe

@sweetfa Bu, dönen türlerde FileNotFound istisnası sorununu önlemek için yaptığım şey : From t As Type In e.Types Where (t IsNot Nothing) AndAlso (t.TypeInitializer IsNot Nothing)Harika çalışıyor gibi görünüyor.
ElektroStudios

22

Bir noktada ReflectionTypeLoadException alınmadan hiçbir şey yapılamayacak gibi görünse de, yukarıdaki yanıtlar sınırlıdır, çünkü istisnadan sağlanan türleri kullanmaya yönelik herhangi bir girişim, türün yüklenememesine neden olan orijinal sorunla hala sorun yaratacaktır.

Bunun üstesinden gelmek için aşağıdaki kod, türleri derlemede bulunanlarla sınırlar ve bir yüklemin türlerin listesini daha da kısıtlamasına izin verir.

    /// <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'u düşündünüz mü ? Ne yapmaya çalıştığını düşünürsek bu yeterli olabilir.


2
Evet bunu düşünmüştüm. Ama kullanmadım çünkü aksi halde herhangi bir bağımlılığı manuel olarak yüklemem gerekirdi. Ayrıca kod ReflectionOnlyLoad ile çalıştırılamaz (bağladığınız sayfadaki Açıklamalar bölümüne bakın).
M4N

3

Benim durumumda, aynı sorun uygulama klasöründeki istenmeyen derlemelerin varlığından kaynaklanıyordu. Bin klasörünü temizlemeyi ve uygulamayı yeniden oluşturmayı deneyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.