Cách ngăn ReflectionTypeLoadException khi gọi Assembly.GetTypes ()


97

Tôi đang cố gắng quét một assembly để tìm các kiểu triển khai giao diện cụ thể bằng cách sử dụng mã tương tự như sau:

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

Vấn đề của tôi là, tôi nhận được một ReflectionTypeLoadExceptionkhi gọi asm.GetTypes()trong một số trường hợp, ví dụ: nếu hợp ngữ chứa các kiểu tham chiếu đến một hợp ngữ hiện không khả dụng.

Trong trường hợp của tôi, tôi không quan tâm đến các loại gây ra sự cố. Các loại tôi đang tìm kiếm không cần các cụm không có sẵn.

Câu hỏi đặt ra là: có thể bằng cách nào đó bỏ qua / bỏ qua các kiểu gây ra ngoại lệ nhưng vẫn xử lý các kiểu khác có trong assembly không?


1
Nó có thể phải viết lại nhiều hơn những gì bạn đang tìm kiếm, nhưng MEF cung cấp cho bạn chức năng tương tự. Chỉ cần đánh dấu từng lớp của bạn bằng thẻ [Xuất] chỉ định giao diện mà nó triển khai. Sau đó, bạn chỉ có thể nhập những giao diện mà bạn quan tâm vào thời điểm đó.
Dirk Dastardly

@Drew, Cảm ơn bình luận của bạn. Tôi đã nghĩ đến việc sử dụng MEF, nhưng muốn xem có giải pháp nào khác rẻ hơn không.
M4N

Đặt cho nhà máy lớp plugin một cái tên nổi tiếng để bạn có thể sử dụng trực tiếp Activator.CreateInstance () là một cách giải quyết đơn giản. Tuy nhiên, nếu bạn nhận được ngoại lệ này ngay bây giờ do vấn đề về độ phân giải lắp ráp thì bạn cũng có thể gặp phải nó sau này.
Hans Passant

1
@Hans: Tôi không chắc mình hoàn toàn hiểu. Hợp ngữ tôi đang quét có thể chứa bất kỳ loại nào triển khai giao diện đã cho, vì vậy không có một loại nào nổi tiếng. (và cũng có thể: Tôi đang quét nhiều hơn một lắp ráp, không chỉ có một)
M4N

2
Tôi có gần như cùng một mã, và cùng một vấn đề. Và bản lắp ráp mà tôi khám phá được đưa ra AppDomain.CurrentDomain.GetAssemblies(), điều này hoạt động trên máy của tôi nhưng không hoạt động trên máy khác. Tại sao một số tổ hợp từ tệp thực thi của tôi không thể đọc / tải được ??
v.oddou

Câu trả lời:


130

Một cách khá khó chịu sẽ là:

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

Tuy nhiên, nó chắc chắn khó chịu khi phải làm điều này. Bạn có thể sử dụng một phương pháp mở rộng để làm cho nó đẹp hơn trong mã "khách hàng":

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

Bạn cũng có thể muốn di chuyển returncâu lệnh ra khỏi khối bắt - bản thân tôi không quan tâm lắm đến việc nó ở đó, nhưng có lẽ nó mã ngắn nhất ...


2
Cảm ơn, đó có vẻ là một giải pháp (và tôi đồng ý, nó có vẻ không phải là một giải pháp sạch).
M4N

4
Giải pháp này vẫn có vấn đề khi bạn cố gắng sử dụng danh sách các loại được hiển thị trong ngoại lệ. Bất kể lý do gì cho ngoại lệ tải kiểu, FileNotFound, BadImage, v.v., sẽ vẫn tiếp tục truy cập vào các kiểu đang được đề cập.
sweetfa 16/10/12

@sweetfa: Vâng, nó rất hạn chế - nhưng nếu OP chỉ cần tìm tên chẳng hạn, thì nó sẽ ổn.
Jon Skeet


@sweetfa Đây là những gì tôi làm để tránh vấn đề ngoại lệ FileNotFound trên các loại trả về: From t As Type In e.Types Where (t IsNot Nothing) AndAlso (t.TypeInitializer IsNot Nothing)Nó có vẻ hoạt động tốt.
ElektroStudios

22

Mặc dù có vẻ như không thể làm gì nếu không nhận được ReflectionTypeLoadException tại một số điểm, các câu trả lời ở trên bị hạn chế ở chỗ bất kỳ nỗ lực nào để sử dụng các loại được cung cấp từ ngoại lệ sẽ vẫn gây ra sự cố ban đầu khiến loại không tải được.

Để khắc phục điều này, đoạn mã sau đây giới hạn các kiểu đối với những kiểu nằm trong hợp ngữ và cho phép một vị từ hạn chế thêm danh sách các kiểu.

    /// <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

Bạn đã xem xét Assembly.ReflectionOnlyLoad chưa? Xem xét những gì bạn đang cố gắng làm, nó có thể là đủ.


2
Vâng, tôi đã xem xét điều đó. Nhưng tôi đã không sử dụng nó vì nếu không tôi sẽ phải tải bất kỳ phụ thuộc nào theo cách thủ công. Ngoài ra, mã sẽ không thực thi được với ReflectionOnlyLoad (xem phần Ghi chú trên trang bạn đã liên kết).
M4N

3

Trong trường hợp của tôi, vấn đề tương tự do sự hiện diện của các cụm không mong muốn trong thư mục ứng dụng. Cố gắng xóa thư mục Bin và xây dựng lại ứng dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.