C# Assembly.Load vs Assembly.ReflectionOnlyLoad

Я пытаюсь понять различия между Assembly.Load и Assembly.ReflectionOnlyLoad.

В приведенном ниже коде я пытаюсь найти все объекты в данной сборке, которые наследуются от данного интерфейса:

var myTypes = new List<Type>();

var assembly = Assembly.Load("MyProject.Components");

foreach (var type in assembly.GetTypes())
{
   if (type.GetInterfaces().Contains(typeof(ISuperInterface)))
   {
      myTypes.Add(type);
   }
}

Этот код прекрасно работает для меня, но я изучал другие, возможно, лучшие альтернативы и наткнулся на метод Assembly.ReflectionOnlyLoad().

Я предположил, что, поскольку я не загружаю и не выполняю какие-либо объекты, по сути, просто запрашиваю их определения, что я мог бы использовать ReflectionOnlyLoad для небольшого увеличения производительности...

Но оказывается, что когда я меняю Assembly.Load на Assembly.ReflectionOnlyLoad, я получаю следующую ошибку при вызове assembly.GetTypes():

System.Reflection.ReflectionTypeLoadException:

Невозможно загрузить один или несколько запрошенных типов. Получите свойство LoaderExceptions для получения дополнительной информации.

Я предположил, что приведенный выше код просто выполнял рефлексию и "смотрел" на библиотеку... но это своего рода пример принципа неопределенности Гейзенберга, в соответствии с которым просмотр библиотеки и объектов в ней на самом деле пытается создать их экземпляр в некоторых путь?

Спасибо Макс

5 ответов

Решение

Согласно ответу Джона, было бы полезно узнать, что в LoaderExceptions, Вместо этой информации, я думаю, я могу рискнуть предположить. Из MSDN:

Если сборка имеет зависимости, метод ReflectionOnlyLoad не загружает их. Если вам нужно проверить их, вы должны загрузить их самостоятельно.

Вам нужно прикрепить обработчик к AppDomain.ReflectionOnlyAssemblyResolve чтобы помочь CLR загрузить любые зависимости сборки, которую вы загружаете. Вы сделали это?

Методы ReflectionOnly - это единственный способ загрузить конкретную сборку на диск для проверки, не используя обычные правила Load/LoadFrom. Например, вы можете загрузить основанную на диске сборку с тем же идентификатором, что и в GAC. Если вы попробовали это с LoadFrom или LoadFile, сборка GAC ВСЕГДА загружается.

Кроме того, вы не можете вызывать GetCustomAttributes (...) в возвращаемом экземпляре Assembly, так как это попытается создать экземпляры Attributes в сборке, которые являются ReflectionOnly. Для этого вы должны использовать статические методы класса CustomAttributeData.

Никакие типы в сборке, загруженной через ReflectionOnly, не могут быть созданы.

Я считаю, что ваше общее понимание различий между Load и ReflectionOnlyLoad правильное. Проблема здесь (я думаю) в том, что даже для простой загрузки типа CLR необходимо считывать метаданные из сборки, в которой определен сам тип, а также загружать метаданные из каждой сборки, в которой определены предки типа. Итак, вам нужно вызывать Assembly.ReflectionOnlyLoad для всех сборок, которые определяют типы, которые являются предками загружаемых вами типов.

Для примера предположим, что у вас есть следующий класс, определенный в сборке A.dll.

public class MyBase
{
   public void Foo() { }
}

и следующий класс, определенный в сборке B.dll.

public class MySubclass : MyBase
{
}

Когда вы вызываете Assembly.GetTypes для сборки B.dll, CLR попытается загрузить тип MySubclass и все его члены. Поскольку метод Foo определен в MyBase в сборке A.dll (и отсутствует в метаданных B.dll), CLR сгенерирует исключения загрузки типов, если сборка A.dll не была загружена.

Ни один метод не может быть выполнен из сборки, загруженной с ReflectionOnlyLoad(), ты получишь InvalidOperationException, Так что это безопасный способ определения содержимого сборки с помощью отражения.

Еще одно большое различие между ними Assembly.Load добавляет сборку в AppDomain в то время как Assembly.ReflectionOnlyLoad не добавит сборку в AppDomain

код, чтобы показать в деталях.

public void AssemblyLoadTest(string assemblyToLoad)
{
    var initialAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4

    Assembly.ReflectionOnlyLoad(assemblyToLoad);
    var reflectionOnlyAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4

    //Shows that assembly is NOT loaded in to AppDomain with Assembly.ReflectionOnlyLoad
    Assert.AreEqual(initialAppDomainAssemblyCount, reflectionOnlyAppDomainAssemblyCount); // 4 == 4

    Assembly.Load(assemblyToLoad);
    var loadAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //5

    //Shows that assembly is loaded in to AppDomain with Assembly.Load
    Assert.AreNotEqual(initialAppDomainAssemblyCount, loadAppDomainAssemblyCount); // 4 != 5
}
Другие вопросы по тегам