Страницы

Поиск по вопросам

Показаны сообщения с ярлыком reflection. Показать все сообщения
Показаны сообщения с ярлыком reflection. Показать все сообщения

пятница, 5 июля 2019 г.

Система плагинов

Захотелось научиться работать с отражением и вот этим всем. Начал с простенького, решил написать подключаемый плагин, который автоматически подгрузится из именной папки.
Что бы не мучиться с отражением методов и прочей нечистью на ум конечно же приходит наследование и полиморфизм. То есть мне нужен какой-то базовый класс с начальным функционалом от которого будут наследоваться все другие плагины.
То есть нужно сделать отдельную библиотеку с апи, которую будут использовать обе стороны (программа и плагин).
public interface IPlugin { void Loaded(); }
Вот такой класс придумал в этой библиотеке. Теперь я могу спокойно ее подключать к плагину и наследоваться.
public class Test : IPlugin { #region Implementation of IPlugin
public void Loaded() => Console.WriteLine( "Hello World" );
#endregion }
Вот у меня уже две библиотеки, одна с моим апи, другая его использует. Теперь мне нужно как-то загрузить сборку с плагином и использовать его методы. Вот тут мне и нужна помощь.
Так как библиотека плагина использует другую библиотеку с апи, я не смогу ее так просто загрузить, так как выскочит ошибка мол либе не хватает референсов. Как правильно загрузить плагин указав что сборка которая ему нужна уже загружена в программу?
Вот код которым я пытаюсь что-то наколдовать:
private static void Main ( string[] args ) { var pluginsPath = $"{Directory.GetCurrentDirectory()}\\Plugins"; var files = Directory.GetFiles( pluginsPath , "*.dll" );
foreach ( var file in files ) { Console.WriteLine( $"Trying load: {file}" ); Assembly assembly;
try { assembly = Assembly.Load( file ); } catch ( Exception ex ) { Console.WriteLine( ex.Message ); continue; }
var types = assembly.GetTypes().Where( type => type.IsClass && type.GetInterface( nameof( IPlugin ) ) != null );
foreach ( var type in types ) { var plugin = Activator.CreateInstance( type ) as IPlugin;
if ( plugin == null ) { Console.WriteLine( "Null" ); continue; }
plugin.Loaded(); } } }
Падает еще на первом try с сообщением:
Необработанное исключение типа "System.IO.FileLoadException" в mscorlib.dll Дополнительные сведения: Не удалось загрузить файл или сборку "C:\Users\anweledig\Documents\Visual Studio 2015\Projects\Anweledig\ConsoleApplication\bin\Debug\Plugins\TestPlugin.dll" либо одну из их зависимостей. Данное имя сборки или база кода недействительны. (Исключение из HRESULT: 0x80131047)


Ответ

Вы можете просто загрузить другую библиотеку. Все ее зависимости будут автоматически загружены.
Проблема может быть только в случае, если библиотека лежит не в той же папке, в которой лежит ваше приложение, и вы при этом загружаете ее через Assembly.LoadFrom. Чтобы это обойти, вам нужно указать рантайму что зависимости надо искать в папке плагина, а не в папке вашего приложения. Тогда можно будет использовать обычный Assembly.Load и заодно заработает станадартный механизм загрузки сборок.
Для этого можно использовать создание аппдомена с указанием AppDomainSetup.ApplicationBase
Т.е. схема примерно такая:
У вас есть базовая dll для плагинов - MyApp.SDK, в ней базовый класс (а лучше - интерфейс):
public abstract class Plugin { public abstract void Loaded(); public abstract void Unload(); }
в ней же код загрузки и вызова плагина (для простоты)
public static class PluginInvoker { public static void InvokePlugin() { var pluginAssembly = Assembly.Load("SomePlugin"); var pluginType = pluginAssembly.GetTypes().Where(t => typeof(Plugin).IsAssignableFrom(t)).Single(); Plugin plugin = (Plugin)pluginType.GetConstructor(new Type[0]).Invoke(null); plugin.Loaded(); } }
есть реализация этого плагина в SomePlugin.dll:
public class Test: MyApp.SDK.Plugin { public override void Loaded() { Console.WriteLine("Loaded"); }
public override void Unload() { Console.WriteLine("Unloaded"); } }
в SomePlugin есть референс на MyApp.SDK.
И есть главное приложение, в котором есть ссылка на MyApp.SDK, но нет ссылки на SomePlugin:
static void Main(string[] args) { AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = @"C:\Projects\MyApp\SomePlugin\bin\Debug\";
AppDomain pluginDomain = AppDomain.CreateDomain("plugin", null, setup); pluginDomain.DoCallBack(PluginInvoker.InvokePlugin); }
ApplicationBase задает папку, откуда AppDomain будет загружать сборки - т.к. это папка плагина, то все, на что он ссылается, будет корректно загружено.
Если плагины подгружаются из подпапки приложения (например, из Plugins), то в вместо AppDomainSetup.ApplicationBase можно использовать AppDomainSetup.PrivateBinPath
Если же при этом плагины вообще не планируется выгружать, и все плагины лежат в одной папке (без подпапок на каждый из плагинов) - то можно обойтись без отдельного AppDomain, просто дописав путь к папке с плагинами в секцию app.config/
код примера на github: https://github.com/PashaPash/PluginLoadSample

суббота, 22 июня 2019 г.

Как создать объект с помощью рефлексии?

Существует несколько вариантов для создания объекта в Java. Слышал, что есть вариант с рефлексией. Как это можно реализовать? А также в чем суть названия этого метода создания объекта?


Ответ

Class.newInstance() Constructor.newInstance() Unsafe.allocateInstance() Десериализация объекта.

пятница, 14 июня 2019 г.

Не могу получить значение аннотации у класса

Итак есть аннотация @MyAnno
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { String value(); }
Почему я не могу получить доступ к ее значению через код clazz.getAnnotation(MyAnno.class) с помощью метода value(),где Class clazz = object.getClass();
При работе с полями класса все ок, то есть этот код работает field.getAnnotation(MyAnno.class).value(), где field это элемент массива Field[] fields = clazz.getDeclaredFields()
P.S clazz.isAnnotationPresent(MyAnno.class) выдает true


Ответ

Предположим, есть такой класс:
@MyAnno("vvv") public class Anno { @MyAnno("xxx") public final int x = 0; }
Тестовый код для получения аннотации этого класса:
public static void main(String[] args) { Object object = new Anno(); Class clazz = object.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { MyAnno myAnno = field.getAnnotation(MyAnno.class); System.out.println("Field value: " + myAnno.value()); } MyAnno myAnno = clazz.getAnnotation(MyAnno.class); System.out.println("Class value: " + myAnno.value()); }
На экран выводится:
Field value: xxx Class value: vvv
Если использовать
Class clazz = object.getClass();
то придётся явно приводить тип:
MyAnno myAnno = (MyAnno)clazz.getAnnotation(MyAnno.class);
Потому что при использовании raw-type (Class) происходит стирание generic-типов в методах, из-за чего метод getAnnotation начинает возвращать просто Annotation

среда, 5 июня 2019 г.

Создание ПО с плагинами

По долгу службы требуется реализация довольно сложного десктопного ПО. Как одно из требований — плагины (подключаемые модули), которые могут быть очень разнообразными: от определения новых типов данных до работы с интерфейсом (добавление форм, кнопок и т.п.). Разумеется, также требуются аналитика, обработка данных, сохранение в разные БД и т.д.
Платформы для выбора — C# или Delphi (т.к. есть прототип, реализующий 90% потребного функционала, но в режиме «спагетти»).
Если с Delphi всё понятно — средство загрузки/выгрузки плагинов делать руками, то вот с C# не всё так прозрачно.
Пока что быстрый поиск показал следующие варианты:
MEF: довольно просто в реализации, но нельзя изолировать плагины в отдельных AppDomain (что довольно-таки важно, это существенное архитектурное преимущество .NET вообще и шарпа в частности); MAF: переусложнено, замудрённо, и, с виду, уже не поддерживается. С другой стороны — мощная изоляция; самостоятельная реализация на reflection (например, так), с возможностью загрузки в AppDomain и прочим. Но придётся много велосипедить, чего в общем-то не хочется.
Есть ещё варианты? Может, что-то не было учтено?
UPD: добавил ссылки.


Ответ

Для C# есть встроенное готовое решение от Microsoft: System.Addin. Позволяет конфигурировать плагины так, чтобы использовать их в том же AppDomain'е, или в выделенном другом, или каждый в своём, или вообще в отдельном процессе.
Не понимаю, почему везде советуют MEF. Это решение именно для расширяемости: код, который вы загружаете, считается доверенным и очень мало отличается от основного кода приложения, поэтому возможности выгрузки и управление правами напрочь отсутствуют.
Самостоятельный велосипед на reflection написать можно, но придётся довольно серьёзно разбираться в тонкостях наподобие Fusion и Load context, так что я бы не советовал тратить время.

Обновление: Ага, System.Addin — это то, что вы называете MAF.

вторник, 14 мая 2019 г.

Запись вызова метода в лог

Есть такой код: public class Foo { [Log("Calling bar")] public void Bar() { } } Как сделать, чтобы при вызове метода Bar что-то писалось в лог? Хотелось бы какое-нибудь событие типа OnCall


Ответ

Аспектно-ориентированное программирование Ведение лога и обработка ошибок — типичные примеры сквозной функциональности. PostSharp - одна из лучших библиотек АОП для c# Вот пример аспекта: public class LogAttribute : PostSharp.Aspects.OnMethodBoundaryAspect { // Выполняется перед входом в метод. public override void OnEntry(MethodExecutionArgs args) { Console.WriteLine("Входим в метод {0} класса {1}", args.Method.Name, args.Method.DeclaringType.FullName);
}
// Выполняется при успешном завершении работы метода public override void OnExit(MethodExecutionArgs args) { Console.WriteLine("Метод {0} класса {1} успешно отработал", args.Method.Name, args.Method.DeclaringType.FullName); } } Быстрый старт PostSharp. Решение задач логгирования и аудита Несколько полезных аспектов для PostSharp

понедельник, 6 мая 2019 г.

Как правильно использовать методы сборки в другом домене?

Если создать домен и загрузить в него сборку, а потом используя метод GetAssemblies, достать сборки, то выгружая домен, сборка остается в основном домене приложения и с ней ничего сделать нельзя, удалить например.
Каким образом можно использовать методы сборки из чужого домена, при этом выгружая домен выгрузить и сборку из приложения?
Вот код которым я это делаю:
using System; using System.IO; using System.Reflection;
namespace Test { internal static class Program { private static void Main() { var appDomain = AppDomain.CreateDomain( "test" ); AssemblyLoader.Run(appDomain);
Console.WriteLine("Сборка загружена");
foreach ( var assembly in appDomain.GetAssemblies () ) { Console.WriteLine ( assembly.GetName () ); }
Console.ReadLine();
AppDomain.Unload( appDomain );
Console.WriteLine("Домен выгружен"); //Не удается удалить сборку из каталога, она не выгрузилась! Console.ReadLine(); } }
public static class AssemblyLoader { public static void Run( AppDomain appDomain ) { appDomain.DoCallBack( Invoke ); }
private static void Invoke() { Assembly.LoadFile( Path.Combine( Environment.CurrentDirectory, "test.dll" ) ); }
}
}


Ответ

Если вы передаёте объект из другого домена, вы этим самым загружаете его в ваш домен, чего вы как раз хотите избежать. Решением может быть передавать примитивные типы. Чтобы не ограничивать себя простыми колбеками, вам нужно воспользоваться MarshalByRefObject (они не копируются между доменами, а выполняются в том домене, где созданы).
Итак, вот рабочий код.
Это плагин, DomainsPlugin.dll:
namespace DomainsPlugin { public class Plugin { public IEnumerable GetNames() { return AppDomain.CurrentDomain.GetAssemblies().Select(asm => asm.GetName()); } } }
Ничего особого, просто рабочий метод.
Теперь главная программа:
namespace DomainsMain { class Program { static void Main(string[] args) { // в этом каталоге бежит программа var executableDir = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location);
// это каталог, где лежит плагин. // у вас он будет лежать, понятно, в другом месте var pluginDir = Path.GetFullPath(Path.Combine( executableDir, "..", "..", "..", "DomainsPlugin", "bin", "Debug"));
// оригинал плагина var pluginSourceFile = Path.Combine(pluginDir, "DomainsPlugin.dll"); // а сюда мы его скопируем, и отсюда будем загружать var pluginWorkingFile = Path.Combine(executableDir, "DomainsPlugin.dll");
// копируем File.Copy(pluginSourceFile, pluginWorkingFile, overwrite: true);
// окей, теперь новый домен var pluginDomain = AppDomain.CreateDomain("Plugin");
// теперь, создаём экземпляр нашего класса в новом домене // у нас на него реально лишь прокси-объект var resident = (Resident)pluginDomain.CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "DomainsMain.Resident"); // получили данные resident.Obtain();
// выводим их foreach (var r in resident.Result) Console.WriteLine(r.ToString());
// выгружаем домен... AppDomain.Unload(pluginDomain);
// ... и удаляем плагин File.Delete(pluginWorkingFile); } }
// простой класс, будет выполняться в другом домене public class Resident : MarshalByRefObject { public List Result;
public void Obtain() { // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable)method.Invoke(p, null); // сохраняем результат. это поле будет доступно из первоначального домена Result = result.ToList(); } } }
На моей машине из-под Visual Studio выдаёт:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Microsoft.VisualStudio.HostingProcess.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a DomainsMain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 DomainsPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Обновление: резидентная функция (Obtain()) вполне может возвращать значение. Но это значение должно быть сериализируемым, чтобы «протиснуться» между доменами.
Для нашего примера, подправляем класс Resident
public class Resident : MarshalByRefObject { public List Obtain() // <-- поменяли возвращаемый тип { // и убрали свойство Result // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable)method.Invoke(p, null); return result.ToList(); } }
Теперь вызов из основного кода выглядит так:
// получили данные var result = resident.Obtain();
// выводим их foreach (var r in result) Console.WriteLine(r.ToString());

понедельник, 29 апреля 2019 г.

Что такое .class?

Constructor constructor = User.class.getConstructor(new Class[]{String.class,String.class,String.class}); System.out.println("Constructor "+constructor); constructor.newInstance("Hello","From","Reflection");
Из данного примера мне понятно что при передачи конструктору создаётся Обьект массива типа Class.
Но вводит в заблуждение: Class.class после чего позволяется вызывать методы Reflection API. Что означает этот .class ?
Я знаю что для доступа к методам надо создать обьект любого класса или если метод статичный то доступ будет произведен через имя класса.. В случае с доступом к Reflection API не понятно это окончание .class, я могу вызвать .class у любого класса или интерфейса или примитива int.class как JVM (если так правильно будет сказать) относится к такого рода конструкциям ? Что это и почему оно используется ?


Ответ

Обращение к .class после имени типа возвращает объект типа Class. Этот объект содержит информацию о типе (будь то класс или интерфейс) и используется в тех местах, где нужно передать информацию о типе (рефлексия как один из примеров).
Вызов .class на типе эквивалентен вызову getClass() на экземпляре типа. Обычно .class используется там, где нужно указать тип, но нет конкретного экземпляра этого типа. Пример для String
System.out.println(String.class.getName()); System.out.println("hello".getClass().getName());

В вашем примере в первой строке вы находите конструктор класса User, который принимает три строковых параметра. Для указания типов параметров используется String.class

среда, 27 февраля 2019 г.

Свой атрибут/описание для public поля класса

Есть у меня классы, у них открытые переменные. Я через reflection добираюсь до их значений для последующего отображения/изменения.
По умолчанию - есть некоторый диапазон для изменения, условно -+20%.
Но для некоторых переменных есть свои особенности: -10%/+20% или 0..100. Это исключения, они не многочисленны, но они есть.
Мне хочется, когда буду перебирать переменные, чтобы можно было прямо в коде задать для переменной свой атрибут или не знаю как это называется - описание, которое я мог бы пропарсить:
public int Length;
// Min: 0. Max: 100 public int Size;
[Range:20%] public int Weight;
Чтобы было более наглядно: у Unity3D есть HideInInspector. Что-то похожее хочу сделать, но более информативно.


Ответ

Используйте для этого кастомные классы атрибутов, они для того и созданы, чтобы предоставлять мета-информацию об объектах в отражениях.
Например, вот атрибут, позволяющий задавать диапазон для полей:
[AttributeUsage(AttributeTargets.Field)] class RangeAttribute : Attribute { public int Percent { get; set; }
public RangeAttribute (int percent) { Percent = percent; } }
Задавать значение диапазона на поле можно так:
class Foo { [Range(20)] public int Bar; }
Ну а дальше — отражения:
FieldInfo barField = typeof(Foo).GetField("Bar"); RangeAttribute barRange = barField.GetCustomAttribute(); int range = barRange != null ? barRange.Percent : 20; var foo = new Foo(); barField.SetValue(foo, 10);
См.:
Attributes Tutorial (MSDN) Writing Custom Attributes (MSDN) Creating Custom Attributes (MSDN)

понедельник, 18 февраля 2019 г.

RTTI. Как обратить к статическому полю класса, имея объект Class для него?

Имеем класс Allocator, который имеет метод allocate(Class). Как без знания того, объект Class какого класса пришёл, вытащить из него поле с возможностью его изменить?
Вариант с использованием objOFclassClass.newInstance() не подойдёт. Охота без лишних затрат памяти на создание объекта обойтись.
class X { public static boolean f = false; }
class Y { public static boolean f = false; }
class Z { public static boolean f = false; }
class Allocator { public static void allocate (Class c) throws Exception{ // Предполагая, что (c.name == "X" или "Y" или "Z"), как сделать что то в духе: // c.setStaticField("f",true); ??? } }


Ответ

С помощью метода getDeclaredField получаем объект класса Field Изменяем поле с помощью вызова метода set у этого объекта

Field field = c.getDeclaredField("имя_поля"); field.set(null, true /* новое значение */);
Код на Ideone

среда, 13 февраля 2019 г.

C# рефлексия. Получение значения приватного поля экземпляра класса?

Есть экземпляр класса Class1, каким образом можно получить его aValue поле? Вопрос крайне глупый, понимаю... и все же. Получается получить только статические поля, а для экземпляров как быть?
public class Class1{
private int aValue; public Class1(int a){ this.aValue = a; } }
Class1 cls = new Class1(10);


Ответ

Нужно указать маску:
Class1 cls = new Class1(10); FieldInfo fieldInfo = typeof(Class1).GetField("aValue", BindingFlags.Instance | BindingFlags.NonPublic); int a = (int)fieldInfo.GetValue(cls);

пятница, 1 февраля 2019 г.

Как проверить, что класс В наследуется от А? (java)

Когда у нас есть объект типа В и класс А - всё просто:
B b; if (b instanceof A) ...
А если у нас нет экземпляра В, а есть только сам класс?
Раньше точно делал, но сейчас найти не могу. Бегу по рефлексии, и мне нужно определить, является ли field.getType() наследником некоторого класса.


Ответ

Судя по этому ответу Нужна функция isAssignableFrom
Super.class.isAssignableFrom(Sub.class)

вторник, 27 ноября 2018 г.

java Как найти всех наследников класса в пакете?

Казалось бы ответ очевиден:
(new Reflections("package")).getSubTypesOf(SuperClass.class)
Однако эта конструкция находит не всех наследников, в случае, если иерархия разнесена на несколько пакетов.
Есть следующие классы:
package1.ParentClass extemds SuperClass package2.ChildClass extends ParentClass
Пакеты package1 и package2 не вложенные.
Конструкция
(new Reflections("package1")).getSubTypesOf(SuperClass.class)
Ожидаемо вернёт package1.ParentClass
А вот конструкция
(new Reflections("package2")).getSubTypesOf(SuperClass.class)
Наш package2.ChildClass не вернёт, хотя это очевидный наследник SuperClass
Я понимаю. что могу в ручную пробежаться по всем классам пакета и собрать нужные. Но неужели нет стандартного способа?
И ещё, объясните, почему по умолчанию ChildClass не находится? Для меня это было очень неожиданное поведение от org.reflections.Reflections
UPD: Изучил исходники ядра org.reflections, всё стало на свои места. Если кратко, java именно так и реализует Reflections. У каждого класса в рефлексии есть только ссылка на родителя. Поэтому поиск подклассов в любом случае перебор. Почему в этом переборе нет супер классов из иных пакетов - вопрос отдельный. Так реализовано.
Для каждой конкретной задачи нужно использовать оптимальное решение. Но хотелось бы услышать о уже существующих решениях, которые позволяют найти всех потомков, кроме java org.reflections.
UPD: Ещё один интересный и нетрудоёмкий способ.
Reflections reflections = new Reflections("package1", new SubTypesScanner()) Set> subTypes = new HashSet<>(); for (String className : reflections.getStore().get(SubTypesScanner.class.getSimpleName()).values()) { try { Class subType = Class.forName(className); if (SuperClass.class.isAssignableFrom(subType)) { subTypes.add(subType); } } catch (ClassNotFoundException e) { throw new RuntimeException("Этого не может быть:)", e); } } return subTypes;
Данное решение перебирает все классы пакета (кроме наследников object)
reflections.getStore().get(SubTypesScanner.class.getSimpleName()).values()
и проверяет на то, что они являются наследником или реализуют SuperClass
SuperClass.class.isAssignableFrom(subType)
По производительности, по крайней мере, лучше, чем поиск по всем пакетам и последующая фильтрация


Ответ

И ещё, объясните, почему по умолчанию ChildClass не находится?
Причина в том, что класс ChildClass является не прямым наследником SuperClass, а через класс ParentClass.
Reflections не уходит рекурсивно на проверку "родителей-родителей" и так далее, поэтому для обнаружения факта наследования через "промежуточные" классы необходимо загрузить пакеты со всеми "промежуточными" классами. В данном случае для того чтобы определить факт наследования ChildClass от SuperClass необходимо так же загрузить ParentClass. Лишние классы из полученного множества(Set) можно отфильтровать по имени пакета.
(new Reflections("package1","package2")) .getSubTypesOf(SuperClass.class) .stream() .filter(c->c.getPackage().getName().equals("package2")).collect(Collectors.toSet());

1) Если неизвестен пакет "package1" то можно загрузить реализации из всех пакетов и потом отфильтровать по нужному пакету "package2":
Set> s = new Reflections( ClasspathHelper.forClass(SuperClass.class)) .getSubTypesOf(SuperClass.class) .stream() .filter(c->c.getPackage().getName().equals("package2")) .collect(Collectors.toSet());
или
Set> s = new Reflections( new ConfigurationBuilder() .setUrls(ClasspathHelper.forClass(SuperClass.class)) .filterInputsBy(new FilterBuilder().excludePackage("package2"))) .getSubTypesOf(SuperClass.class);
2) В качестве более производительной альтернативы можно воспользоваться стандартными средствами “java core” и получить все файлы-классы пакета и их уже рекурсивно проверить на наличие искомого наследника.
public static Set> loadClasses(String packageName) throws ClassNotFoundException { Set> classes = new HashSet<>(); URL resource = Thread.currentThread() .getContextClassLoader() .getResource(packageName.replace('.', '/')); File directory = new File(resource.getFile()); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); if (files == null || files.length == 0) { return classes; } for (File file : files) { //в другие пакеты уходить ненужно if (file.isFile() && file.getName().endsWith(".class")) { classes.add(Class .forName(String.format("%s.%s", packageName, file.getName().substring(0, file.getName().indexOf("."))))); } } return classes; }

public static boolean checkAllParents(Class type, Class hasParentType) { Class parent = type.getSuperclass(); if (parent == Object.class) { return false; } if (parent.getClass() == hasParentType.getClass()) { return true; } return checkAllParents(parent, hasParentType); }

Set> s = loadClasses("package2").stream() .filter(i->checkAllParents(i, SuperClass.class)) .collect(Collectors.toSet());

понедельник, 12 ноября 2018 г.

Определение свойства, по которому нужно произвести фильтр

Здравствуйте. Возник очень тяжелый вопрос с которым я никогда не сталкивался. мне нужно создать метод, который поймет по какому свойству нужно сделать фильтр. Т.е., допустим у меня есть класс
public class User { public string Name {get; set;} public string Nick {get; set;} }
И мне нужно вытащить из базы некоторых пользователей но критерий заранее не известен, в запросе Name или Nick могут быть null.
В данный момент это выглядит примерно так:
//это часть когда находится в классе user IQarable query ... тут создается query и передается в метод ниже ...
if (!string.IsNullOrEmpty(Name)) { query = query.Where(x => x.VenueName.Contains(Venue)); }
if (!string.IsNullOrEmpty(Nick)) { query = query.Where(x => x.City.Contains(City));
} //и так далее
Внутри блоков If есть еще кое какая проверка, вот поэтому я пытаюсь это вынести в 1 метод, но не в этом суть.
Я пытаюсь сделать метод, который принимает query и свойство в виде строки, по которому нужно выполнить Where(...), что бы это выглядело так
if (!string.IsNullOrEmpty(Name)) { query = SearchMethod(query, "Name", "Jhon"); }
Я не могу представить как мне заменить выражение Where(x => x./*тут свойство, которое каким-то образом определено*/.Contais("SearchingValue")) что-то другое, что может вычислить свойство по которому я веду поиск, и подставить его в это выражение. По рефлексии я смог получить только само свойство.
Type t = this.GetType(); PropertyInfo prop = t.GetProperty("EventName");
Прошу вашей помощи в решении этой проблемы.


Ответ

Отфильтровать IQueryable по Func нельзя. (У вас получится IEnumerable.) Для сохранения IQueryable вам придётся строить Expression (и кажется, вручную). Вот документация
Для вашего случая, если нужно сравнивать значение с константой, можно сделать так (не тестировал, возможны вылеты в рантайме):
IQueryable Filter(IQueryable original, Expression> еxtractor, V value) { return original.Where(ProjectionEquals(еxtractor, value)); }
Expression> ProjectionEquals(Expression> еxtractor, V value) { var body = Expression.Equal(еxtractor.Body, Expression.Constant(value)); return Expression.Lambda>(body, еxtractor.Parameters[0]); }
Пользоваться так:
query = Filter(query, x => x.Name, "Jhon");
Внутри Filter можно накрутить, понятно, более сложную логику.

Если всё же очень хочется потерять проверки на этапе компиляции и передавать имена свойств как строки, можно так:
IQueryable Filter(IQueryable original, string propName, V value) { return original.Where(PropertyEquals(propName, value)); }
Expression> PropertyEquals(string propName, V value) { var parameter = Expression.Parameter(typeof(T), "t"); var left = Expression.PropertyOrField(parameter, propName); var body = Expression.Equal(left, Expression.Constant(value)); return Expression.Lambda>(body, parameter); }
и пользоваться так:
query = Filter(query, "Name", "Jhon");
Понятна схема?

Для примера, если вам нужно Contains
Expression> GetContains(string propName, string value) { var parameter = Expression.Parameter(typeof(T), "t"); var prop = Expression.Property(parameter, propName); var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var valueAsExpr = Expression.Constant(value, typeof(string)); var contains = Expression.Call(propOrField, containsMethod, valueAsExpr); return Expression.Lambda>(contains, parameter); }

Как подсказывает в комментарии @Pavel Mayorov, последнюю функцию можно переписать проще:
Expression> GetContains(string propName, string value) { var parameter = Expression.Parameter(typeof(T), "t"); var prop = Expression.Property(parameter, propName); var valueAsExpr = Expression.Constant(value, typeof(string)); var contains = Expression.Call(prop, "Contains", null, valueAsExpr); return Expression.Lambda>(contains, parameter); }

вторник, 23 октября 2018 г.

Сравнение алгоритмов проверки переменной на значение по умолчанию

Update: Самым быстрым оказался алгоритм V7
Продолжение предыдущего вопроса.
Есть несколько вариантов реализации функции для проверки переменной на значение по умолчанию. Из комментариев к предыдущему вопросу я понял лишь то, что мне еще очень много предстоит выучить в C#. Помогите пожалуйста найти самый производительный и надежный вариант реализации функции.
В качестве параметра функции может быть все что угодно, включая класс, структуру, типы на подобие int? и так далее.
Некоторые варианты реализации функции:
V1:
bool IsDefault(T o) { if (o == null) // => ссылочный тип или nullable return true; if (Nullable.GetUnderlyingType(typeof(T)) != null) // nullable, не null return false; var type = o.GetType(); //для .net core type.GetTypeInfo().IsClass if (type.IsClass) return false; else // => тип-значение, есть конструктор по умолчанию return Activator.CreateInstance(type).Equals(o); }
V3 (улучшенный)
bool isDefault(T o) { return (o==null)?true: // считаем что null default o.GetType().IsValueType && !typeof(T).IsGenericType ? o.Equals(Activator.CreateInstance(o.GetType())): // default типа который внутри nullable o.Equals(default(T)); // настоящий default }
V5 смешать V1 и большое кол-во перегрузок под все типы:
public class DefaultChecker { bool IsDefault(byte value) => value == 0; bool IsDefault(byte? value) => value == null; bool IsDefault(sbyte value) => value == 0; bool IsDefault(sbyte? value) => value == null; bool IsDefault(int value) => value == 0; bool IsDefault(int? value) => value == null; bool IsDefault(uint value) => value == 0; bool IsDefault(uint? value) => value == null; bool IsDefault(short value) => value == 0; bool IsDefault(short? value) => value == null; bool IsDefault(ushort value) => value == 0; bool IsDefault(ushort? value) => value == null; bool IsDefault(long value) => value == 0; bool IsDefault(long? value) => value == null; bool IsDefault(ulong value) => value == 0; bool IsDefault(ulong? value) => value == null; bool IsDefault(float value) => value == 0.0F; bool IsDefault(float? value) => value == null; bool IsDefault(double value) => value == 0.0D; bool IsDefault(double? value) => value == null; bool IsDefault(char value) => value == '\0'; bool IsDefault(char? value) => value == null; bool IsDefault(bool value) => !value; bool IsDefault(bool? value) => value == null; bool IsDefault(string value) => value == null; bool IsDefault(decimal value) => value == 0.0M; bool IsDefault(decimal? value) => value == null;
public bool IsDefault(T value) { if (value == null) // => ссылочный тип или nullable return true; if (Nullable.GetUnderlyingType(typeof(T)) != null) // nullable, не null return false; var type = value.GetType(); //для .net core type.GetTypeInfo().IsClass if (type.IsClass) return false; else // => тип-значение, есть конструктор по умолчанию return Activator.CreateInstance(type).Equals(value); } }
V6:
class RequireStruct where T : struct { } class RequireClass where T : class { }
static bool IsDefault(T o, RequireClass ignore = null) where T : class { if (o == null) return true; if (!(o is ValueType)) // не упакованная ли это структура? return false; // нет - выходим return Activator.CreateInstance(o.GetType()).Equals(o); // медленный путь }
static bool IsDefault(T? o) where T : struct => o == null;
static bool IsDefault(T o, RequireStruct ignore = null) where T : struct => default(T).Equals(o); // default(T) не требует рефлексии
V7 смешать V6 и большое кол-во перегрузок под все типы:
bool IsDefault(byte value) => value == 0; bool IsDefault(byte? value) => value == null; bool IsDefault(sbyte value) => value == 0; bool IsDefault(sbyte? value) => value == null; bool IsDefault(int value) => value == 0; bool IsDefault(int? value) => value == null; bool IsDefault(uint value) => value == 0; bool IsDefault(uint? value) => value == null; bool IsDefault(short value) => value == 0; bool IsDefault(short? value) => value == null; bool IsDefault(ushort value) => value == 0; bool IsDefault(ushort? value) => value == null; bool IsDefault(long value) => value == 0; bool IsDefault(long? value) => value == null; bool IsDefault(ulong value) => value == 0; bool IsDefault(ulong? value) => value == null; bool IsDefault(float value) => value == 0.0F; bool IsDefault(float? value) => value == null; bool IsDefault(double value) => value == 0.0D; bool IsDefault(double? value) => value == null; bool IsDefault(char value) => value == '\0'; bool IsDefault(char? value) => value == null; bool IsDefault(bool value) => !value; bool IsDefault(bool? value) => value == null; bool IsDefault(string value) => value == null; bool IsDefault(decimal value) => value == 0.0M; bool IsDefault(decimal? value) => value == null; bool IsDefault(T o, RequireClass ignore = null) where T : class { if (o == null) return true; if (!(o is ValueType)) return false; return Activator.CreateInstance(o.GetType()).Equals(o); } bool IsDefault(T? o) where T : struct => o == null; bool IsDefault(T o, RequireStruct ignore = null) where T : struct => default(T).Equals(o);
---Реализации, которые не проходят тест---
V2:
public object GetDefaultValue(Type target) { Expression> e = Expression.Lambda>( Expression.Convert( Expression.Default(target), typeof(object))); return e.Compile()(); }
public bool IsDefault(object o) { if(o == null) throw new ArgumentNullException(nameof(o));
return o.Equals(GetDefaultValue(o.GetType())); }
V3:
bool isDefault(T o) { return (o==null)?true: o.GetType().IsValueType ? Activator.CreateInstance(o.GetType()).Equals(o) : o.Equals(default(T)); }
V4:
//isDefault((object)0) этот вариант даст false bool isDefault(T o) { return (o==null)?(default(T)==null):o.Equals(default(T)); }


Ответ

Для начала, нужно составить юнит-тесты на желаемую функцию. Например, такие:
Debug.Assert(IsDefault(default(string))); Debug.Assert(IsDefault((object)default(string))); Debug.Assert(!IsDefault(string.Empty)); Debug.Assert(!IsDefault((object)string.Empty)); Debug.Assert(IsDefault(default(int))); Debug.Assert(!IsDefault(1)); Debug.Assert(IsDefault(default(int?))); Debug.Assert(!IsDefault((int?)0)); Debug.Assert(IsDefault((object)0)); Debug.Assert(!IsDefault((object)1));
Пробуем различные имплементации:
V1 проходит. V2 бросает ArgumentNullException вместо того, чтобы вернуть false, на Debug.Assert(IsDefault(default(string))); V3 не проходит Debug.Assert(!IsDefault((int?)0)); проходит. V4 не проходит Debug.Assert(IsDefault((object)0)) V5 проходит. V6 проходит. V7 проходит.

Окей, на текущий момент тесты проходят V1, V3, V5, V6 и V7. Тестировалось при помощи BenchmarkDotNet, результаты ниже. Вот результаты:
BenchmarkDotNet=v0.10.9, OS=Windows 10 Redstone 2 (10.0.15063) Processor=Intel Core i7-6700 CPU 3.40GHz (Skylake), ProcessorCount=8 Frequency=3328123 Hz, Resolution=300.4697 ns, Timer=TSC [Host] : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2102.0 DefaultJob : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2102.0
Method | Mean | Error | StdDev | ------- |-----------:|---------:|---------:| TestV1 | 1,793.9 ns | 4.960 ns | 4.639 ns | TestV3 | 1,260.1 ns | 6.523 ns | 5.782 ns | TestV5 | 1,195.5 ns | 9.269 ns | 7.740 ns | TestV6 | 481.8 ns | 2.526 ns | 2.363 ns | TestV7 | 445.3 ns | 1.847 ns | 1.728 ns |
Обратите внимание, что код проверки пробегает в худшем случае за менее двух, а в лучшем — за половину микросекунды (то есть, одной миллионной части секунды). Поэтому искать выигрыш в любой из реализаций нет особенного смысла: все реализации пробегают очень быстро. Единственный случай, при котором имеет смысл задуматься об оптимизации — это если такая вот операция вызывается десятки тысяч раз. (Но в этом случае, возможно, имеет смысл пересмотреть дизайн программы.)
Если кому интересно, вот код сравнения (длинный и скучный):
using System; using System.Collections.Generic; using System.Diagnostics; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running;
namespace Т { public class Program { public static void Main(string[] args) { var summary = BenchmarkRunner.Run(); } }
public class Benchmarks { bool IsDefaultV1(T o) { if (o == null) // => ссылочный тип или nullable return true; if (Nullable.GetUnderlyingType(typeof(T)) != null) // nullable, не null return false; var type = o.GetType(); if (type.IsClass) return false; else // => тип-значение, есть конструктор по умолчанию return Activator.CreateInstance(type).Equals(o); }
bool IsDefaultV3(T o) { return (o == null) ? true : // считаем что null default o.GetType().IsValueType && !typeof(T).IsGenericType ? o.Equals(Activator.CreateInstance(o.GetType())) : // default типа который внутри nullable o.Equals(default(T)); // настоящий default }
public bool IsDefaultV5(byte value) => value == 0; public bool IsDefaultV5(byte? value) => value == null; public bool IsDefaultV5(sbyte value) => value == 0; public bool IsDefaultV5(sbyte? value) => value == null; public bool IsDefaultV5(int value) => value == 0; public bool IsDefaultV5(int? value) => value == null; public bool IsDefaultV5(uint value) => value == 0; public bool IsDefaultV5(uint? value) => value == null; public bool IsDefaultV5(short value) => value == 0; public bool IsDefaultV5(short? value) => value == null; public bool IsDefaultV5(ushort value) => value == 0; public bool IsDefaultV5(ushort? value) => value == null; public bool IsDefaultV5(long value) => value == 0; public bool IsDefaultV5(long? value) => value == null; public bool IsDefaultV5(ulong value) => value == 0; public bool IsDefaultV5(ulong? value) => value == null; public bool IsDefaultV5(float value) => value == 0.0F; public bool IsDefaultV5(float? value) => value == null; public bool IsDefaultV5(double value) => value == 0.0D; public bool IsDefaultV5(double? value) => value == null; public bool IsDefaultV5(char value) => value == '\0'; public bool IsDefaultV5(char? value) => value == null; public bool IsDefaultV5(bool value) => !value; public bool IsDefaultV5(bool? value) => value == null; public bool IsDefaultV5(string value) => value == null; public bool IsDefaultV5(decimal value) => value == 0.0M; public bool IsDefaultV5(decimal? value) => value == null; public bool IsDefaultV5(T value) { if (value == null) // => ссылочный тип или nullable return true; if (Nullable.GetUnderlyingType(typeof(T)) != null) // nullable, не null return false; var type = value.GetType(); //для .net core type.GetTypeInfo().IsClass if (type.IsClass) return false; else // => тип-значение, есть конструктор по умолчанию return Activator.CreateInstance(type).Equals(value); }
class RequireStruct where T : struct { } class RequireClass where T : class { }
static bool IsDefaultV6(T o, RequireClass ignore = null) where T : class { if (o == null) return true; if (!(o is ValueType)) return false; return Activator.CreateInstance(o.GetType()).Equals(o); } static bool IsDefaultV6(T? o) where T : struct => o == null; static bool IsDefaultV6(T o, RequireStruct ignore = null) where T : struct => default(T).Equals(o);
static bool IsDefaultV7(byte value) => value == 0; static bool IsDefaultV7(byte? value) => value == null; static bool IsDefaultV7(sbyte value) => value == 0; static bool IsDefaultV7(sbyte? value) => value == null; static bool IsDefaultV7(int value) => value == 0; static bool IsDefaultV7(int? value) => value == null; static bool IsDefaultV7(uint value) => value == 0; static bool IsDefaultV7(uint? value) => value == null; static bool IsDefaultV7(short value) => value == 0; static bool IsDefaultV7(short? value) => value == null; static bool IsDefaultV7(ushort value) => value == 0; static bool IsDefaultV7(ushort? value) => value == null; static bool IsDefaultV7(long value) => value == 0; static bool IsDefaultV7(long? value) => value == null; static bool IsDefaultV7(ulong value) => value == 0; static bool IsDefaultV7(ulong? value) => value == null; static bool IsDefaultV7(float value) => value == 0.0F; static bool IsDefaultV7(float? value) => value == null; static bool IsDefaultV7(double value) => value == 0.0D; static bool IsDefaultV7(double? value) => value == null; static bool IsDefaultV7(char value) => value == '\0'; static bool IsDefaultV7(char? value) => value == null; static bool IsDefaultV7(bool value) => !value; static bool IsDefaultV7(bool? value) => value == null; static bool IsDefaultV7(string value) => value == null; static bool IsDefaultV7(decimal value) => value == 0.0M; static bool IsDefaultV7(decimal? value) => value == null; static bool IsDefaultV7(T o, RequireClass ignore = null) where T : class { if (o == null) return true; if (!(o is ValueType)) return false; return Activator.CreateInstance(o.GetType()).Equals(o); } static bool IsDefaultV7(T? o) where T : struct => o == null; static bool IsDefaultV7(T o, RequireStruct ignore = null) where T : struct => default(T).Equals(o);
struct Test { int x; public Test(int x) { this.x = x; } }
List l1 = default; List l2 = new List(); string s1 = default; string s2 = string.Empty; int i1 = default; int i2 = 1; int? ni1 = default; int? ni2 = 0; object bi1 = 0; object bi2 = 1; Test t1 = default; Test t2 = new Test(1); object tb1 = default(Test); object tb2 = new Test(1); ValueType tc1 = default(Test); ValueType tc2 = new Test(1);
[Benchmark] public int TestV1() { return (IsDefaultV1(l1) ? 1 : 0) + (!IsDefaultV1(l2) ? 1 : 0) + (IsDefaultV1(s1) ? 1 : 0) + (!IsDefaultV1(s2) ? 1 : 0) + (IsDefaultV1(i1) ? 1 : 0) + (!IsDefaultV1(i2) ? 1 : 0) + (IsDefaultV1(ni1) ? 1 : 0) + (!IsDefaultV1(ni2) ? 1 : 0) + (IsDefaultV1(bi1) ? 1 : 0) + (!IsDefaultV1(bi2) ? 1 : 0) + (IsDefaultV1(t1) ? 1 : 0) + (!IsDefaultV1(t2) ? 1 : 0) + (IsDefaultV1(tb1) ? 1 : 0) + (!IsDefaultV1(tb2) ? 1 : 0) + (IsDefaultV1(tc1) ? 1 : 0) + (!IsDefaultV1(tc2) ? 1 : 0); }
[Benchmark] public int TestV3() { return (IsDefaultV3(l1) ? 1 : 0) + (!IsDefaultV3(l2) ? 1 : 0) + (IsDefaultV3(s1) ? 1 : 0) + (!IsDefaultV3(s2) ? 1 : 0) + (IsDefaultV3(i1) ? 1 : 0) + (!IsDefaultV3(i2) ? 1 : 0) + (IsDefaultV3(ni1) ? 1 : 0) + (!IsDefaultV3(ni2) ? 1 : 0) + (IsDefaultV3(bi1) ? 1 : 0) + (!IsDefaultV3(bi2) ? 1 : 0) + (IsDefaultV3(t1) ? 1 : 0) + (!IsDefaultV3(t2) ? 1 : 0) + (IsDefaultV3(tb1) ? 1 : 0) + (!IsDefaultV3(tb2) ? 1 : 0) + (IsDefaultV3(tc1) ? 1 : 0) + (!IsDefaultV3(tc2) ? 1 : 0); }
[Benchmark] public int TestV5() { return (IsDefaultV5(l1) ? 1 : 0) + (!IsDefaultV5(l2) ? 1 : 0) + (IsDefaultV5(s1) ? 1 : 0) + (!IsDefaultV5(s2) ? 1 : 0) + (IsDefaultV5(i1) ? 1 : 0) + (!IsDefaultV5(i2) ? 1 : 0) + (IsDefaultV5(ni1) ? 1 : 0) + (!IsDefaultV5(ni2) ? 1 : 0) + (IsDefaultV5(bi1) ? 1 : 0) + (!IsDefaultV5(bi2) ? 1 : 0) + (IsDefaultV5(t1) ? 1 : 0) + (!IsDefaultV5(t2) ? 1 : 0) + (IsDefaultV5(tb1) ? 1 : 0) + (!IsDefaultV5(tb2) ? 1 : 0) + (IsDefaultV5(tc1) ? 1 : 0) + (!IsDefaultV5(tc2) ? 1 : 0); }
[Benchmark] public int TestV6() { return (IsDefaultV6(l1) ? 1 : 0) + (!IsDefaultV6(l2) ? 1 : 0) + (IsDefaultV6(s1) ? 1 : 0) + (!IsDefaultV6(s2) ? 1 : 0) + (IsDefaultV6(i1) ? 1 : 0) + (!IsDefaultV6(i2) ? 1 : 0) + (IsDefaultV6(ni1) ? 1 : 0) + (!IsDefaultV6(ni2) ? 1 : 0) + (IsDefaultV6(bi1) ? 1 : 0) + (!IsDefaultV6(bi2) ? 1 : 0) + (IsDefaultV6(t1) ? 1 : 0) + (!IsDefaultV6(t2) ? 1 : 0) + (IsDefaultV6(tb1) ? 1 : 0) + (!IsDefaultV6(tb2) ? 1 : 0) + (IsDefaultV6(tc1) ? 1 : 0) + (!IsDefaultV6(tc2) ? 1 : 0); }
[Benchmark] public int TestV7() { return (IsDefaultV7(l1) ? 1 : 0) + (!IsDefaultV7(l2) ? 1 : 0) + (IsDefaultV7(s1) ? 1 : 0) + (!IsDefaultV7(s2) ? 1 : 0) + (IsDefaultV7(i1) ? 1 : 0) + (!IsDefaultV7(i2) ? 1 : 0) + (IsDefaultV7(ni1) ? 1 : 0) + (!IsDefaultV7(ni2) ? 1 : 0) + (IsDefaultV7(bi1) ? 1 : 0) + (!IsDefaultV7(bi2) ? 1 : 0) + (IsDefaultV7(t1) ? 1 : 0) + (!IsDefaultV7(t2) ? 1 : 0) + (IsDefaultV7(tb1) ? 1 : 0) + (!IsDefaultV7(tb2) ? 1 : 0) + (IsDefaultV7(tc1) ? 1 : 0) + (!IsDefaultV7(tc2) ? 1 : 0); }
public void CheckCorrectness() { var testMethods = this.GetType() .GetMethods() .Where(m => m.Name.StartsWith("TestV")) .Select(m => (name: m.Name, func: (Func)m.CreateDelegate(typeof(Func), this))) .ToList(); foreach ((var name, var func) in testMethods) { Console.Write($"Testing {name}... "); var ok = func() == 16; Console.WriteLine(ok ? "success" : "FAILED"); } } } }

Что происходит со сборкой?

Получилась следующая ситуация...
1) Создаем в приложении домен. 2) Загружаем в него через DoCallBack сборку Assembly.Load(...) 3) Из главного домена достаем сборки ранее созданного и например, показываем их имена. 4) Смотрим какие сборки есть в главном домене, а конкретней - там появилась эта сборка которую мы загрузили в другой домен.
То есть, загружая сборку в другой домен, если обратиться с методу GetAssemblies, загруженные сборки появляются и в основном домене, при этом они не просто туда переносятся, они копируются - их можно использоваться в обоих доменах.
Если зациклить данный процесс и наблюдать за памятью, то видно что она растет, значит сборки загружаются по новой в память каждый раз.
Самый главный вопрос, почему? Как избавиться от переноса сборки в основной домен? Тут даже не в памяти дело, а в том что основной домен держит референс на сборку и не дает её удалить, например. А инфу как-то вытаскивать надо.
class Program { static void Main ( string [ ] args ) { var appDomain = AppDomain.CreateDomain("TestDomain"); appDomain.DoCallBack(LoadModule);
var assembly = appDomain.GetAssemblies().Single( t => t.GetName().Name == "TestModule" );
foreach ( var assembly1 in AppDomain.CurrentDomain.GetAssemblies() ) { Console.WriteLine( assembly1.FullName ); } }
private static void LoadModule() { Assembly.Load( "TestModule" ); } }


Ответ

Существует две возможности передать объект через границу домена приложения:
Через прокси. Для объектов наследующих от System.MarshalByRefObject. При этом все вызовы методов на прокси объекте автоматически перенаправляются в домен приложения, которому принадлежит исходный объект. Через бинарную сериализацию. Для объектов, которые её поддерживают. При этом создаётся копия объекта, которая более не связана с исходным объектом.
При этом класс System.AppDomain наследует от System.MarshalByRefObject, в то время как класс System.Reflection.Assembly нет. То есть вызов appDomain.GetAssemblies() в Вашем коде будет перенаправлен в дополнительный домен приложения, после чего результат его работы (Assembly[]) необходимо будет передать в основной домен приложения: сериализовать в дополнительном домене и десериализовать в основном домене. Данная передача Assembly объектов в основной домен приложения и приводит к загрузке в него сборок.
Чтобы избежать загрузки сборок в основной домен, не передавайте в него объекты, для десериализации которых необходимо загрузить сборку. Как то System.Reflection.Assembly объект, представляющий сборку, System.Type объект, представляющий любой тип из сборки, экземпляр любого типа из сборки.
Например имена сборок можно извлекать в дополнительном домене приложения, а в основной передавать только строки:
using System; using System.Linq; using System.Reflection;
class Program { static void Main ( string [ ] args ) { var appDomain = AppDomain.CreateDomain("TestDomain"); appDomain.DoCallBack(LoadModule);
var worker = (Worker)appDomain.CreateInstanceAndUnwrap(typeof(Worker).Assembly.FullName, typeof(Worker).FullName);
foreach ( var assemblyFullName1 in worker.GetAssembliesFullName() ) { Console.WriteLine( assemblyFullName1 ); } Console.WriteLine(); foreach ( var assembly1 in AppDomain.CurrentDomain.GetAssemblies() ) { Console.WriteLine( assembly1.FullName ); } }
private static void LoadModule() { Assembly.Load( "TestModule" ); } }
class Worker : MarshalByRefObject { public string[] GetAssembliesFullName() { return AppDomain.CurrentDomain.GetAssemblies().Select(a => a.FullName).ToArray(); } }

среда, 10 октября 2018 г.

Способы создания объектов в C#

Я знаю 1 способ создать объекта в C#:
public static class ObjectCreator { public static T GetObject() where T : class { return (T)Activator.CreateInstance(typeof(T)); } }
Есть ли более производительные?
Update класс с известными способами:
public class ObjectCreator where T: new() { protected Func V4Lambda;
protected Func V5Lambda;
public ObjectCreator() { Type sType = typeof(T);
//V4 V4Lambda = Expression.Lambda>(Expression.New(sType)).Compile();
//V5 V5Lambda = DynamicModuleLambdaCompiler.GenerateFactory(); }
public T V1() { return (T)Activator.CreateInstance(typeof(T)); }
public T V2() { return new T(); }
public T V3() { return CustomActivator.CreateInstance(); }
public T V4() { return V4Lambda(); }
public T V5() { return V5Lambda(); } }
public static class CustomActivator { public static T CreateInstance() where T : new() { return ActivatorImpl.Factory(); }
private class ActivatorImpl where T : new() { private static readonly Expression> _expr = () => new T();
public static readonly Func Factory = _expr.Compile(); } }
public static class DynamicModuleLambdaCompiler { public static Func GenerateFactory() where T : new() { Expression> expr = () => new T(); NewExpression newExpr = (NewExpression)expr.Body;
var method = new DynamicMethod( name: "lambda", returnType: newExpr.Type, parameterTypes: new Type[0], m: typeof(DynamicModuleLambdaCompiler).Module, skipVisibility: true);
ILGenerator ilGen = method.GetILGenerator(); // Constructor for value types could be null if (newExpr.Constructor != null) { ilGen.Emit(OpCodes.Newobj, newExpr.Constructor); } else { LocalBuilder temp = ilGen.DeclareLocal(newExpr.Type); ilGen.Emit(OpCodes.Ldloca, temp); ilGen.Emit(OpCodes.Initobj, newExpr.Type); ilGen.Emit(OpCodes.Ldloc, temp); }
ilGen.Emit(OpCodes.Ret);
return (Func)method.CreateDelegate(typeof(Func)); } }
Результаты моего тестирования:


Ответ

В следующих статьях Сергея Теплякова
Исследуем new() ограничение в C# Dissecting the new() constraint in C#: a perfect example of a leaky abstraction
делается вывод, что самый быстрый способ создания объекта - это распарсить лямбду в выражение и скомпилировать его:
public static class DynamicModuleLambdaCompiler { public static Func GenerateFactory() where T:new() { Expression> expr = () => new T(); NewExpression newExpr = (NewExpression)expr.Body;
var method = new DynamicMethod( name: "lambda", returnType: newExpr.Type, parameterTypes: new Type[0], m: typeof(DynamicModuleLambdaCompiler).Module, skipVisibility: true);
ILGenerator ilGen = method.GetILGenerator(); // Constructor for value types could be null if (newExpr.Constructor != null) { ilGen.Emit(OpCodes.Newobj, newExpr.Constructor); } else { LocalBuilder temp = ilGen.DeclareLocal(newExpr.Type); ilGen.Emit(OpCodes.Ldloca, temp); ilGen.Emit(OpCodes.Initobj, newExpr.Type); ilGen.Emit(OpCodes.Ldloc, temp); }
ilGen.Emit(OpCodes.Ret);
return (Func)method.CreateDelegate(typeof(Func)); } }

public static class FastActivator where T : new() { ///

/// Extremely fast generic factory method that returns an instance /// of the type . /// public static readonly Func Create = DynamicModuleLambdaCompiler.GenerateFactory(); }

Method | Mean | StdDev | Gen 0 | ------------------------ |----------- |---------- |------- | ActivatorCreateInstance | 95.0161 ns | 1.0861 ns | 0.0005 | FuncBasedFactory | 6.5741 ns | 0.0608 ns | 0.0034 | FastActivator_T_Create | 5.1715 ns | 0.0466 ns | 0.0034 |