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
}