Страницы

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

вторник, 26 ноября 2019 г.

Когда может пригодится пространство имен System.Reflection.Emit?


Когда может пригодится пространство имен System.Reflection.Emit ?

Вроде, в .NET предусмотрены классы, которые позволяют генерировать динамически сборки на высокоуровневом языке.

Зачем погружаться в низкий уровень?
    


Ответы

Ответ 1



Reflection.Emit — это для тех, кто хочет создавать динамические сборки (а не добавлять что-то в существующие) на чистом IL (хардкор короче). Подробнее ниже. А может вам надо что-то другое и желательно попроще? Если вам просто нужен динамический тип, может вам просто подойдёт dynamic или ExpandoObject? И никакой генерации кода, правда о проверке на этапе компиляции можно забыть. Вам нужен метод (может также их группа), который будет принимать и анализироват лямбда выражения? Тогда используйте Expression Trees. Для простых случаев будет хорошо, но если вы надумали написать свой ORM, то это занятие совсем не из простых. При этом помните, что для Expression Trees поддерживаются только однострочные лямбда-выражения. Если вам нужен динамически генерируемый HTML (вдруг), а это не веб-приложение, т используйте RazorEngine. Также симулируя веб-приложение, можно добавить поддержку Intellisense. Генерация кода Если вкратце: если вам нужна всё же полноценная генерация кода, то используйте Roslyn Потому что мощный и платформенно независимый. Возможно также будет полезен T4 (особенно если нужен не-C# и не-VB код). Преимущества и недостатки Reflection.Emit Преимущества создание динамической сборки на чистом IL: вы полностью контролируете код и может писать высоко оптимизированный код. Преимущество тут ещё в том, что если есть какая-то возможность недоступная в C#, но доступная в CLR, вы тоже сможете её задействовать. хорошая производительность генерации кода. Созданный вами код сразу компилируетс в сборку (в отличие от Microsoft.CodeDom, но c Roslyn всё хорошо, ибо он дёргает компилятор как сервис). платформенно-независимый подход (в отличие от Microsoft.CodeDom, который специфичен только для реализации Microsoft, но Roslyn тоже платформенно независим) Недостатки приходится прописывать простейшие вещи вроде циклов (просто серия переходов). Вам приходится думать на низком уровне. Поэтому сложно создавать нетривиальные программы. вы обязаны генерировать новую сборку. Если вам это подходит, то это не недостаток Но если вы хотите что-то добавить в существующую, то это невозможно средствами Reflection.Emit. Исходя из этого решайте применять вам Reflection.Emit или нет. Но я полагаю, чт это совсем хардкор. Альтернативы Microsoft.CodeDom Roslyn T4 Microsoft.CodeDom vs Reflection.Emit Способы программирования Сгенерированный код с помощью Microsoft.CodeDom может быть частью решения (класс LINQ to SQL, WSDL, XSD сгенерированы таким способом). Вы также можете использовать partial классы для настойки сгенерированного кода. Когда используете Reflection.Emit вы обязаны создавать новую сборку. Когда вы используете Reflecion.Emit вы программируете с помощью IL-команд. Когд вы программируете c помощью Microsoft.CodeDom вы создаёте команды C#. Поэтому код Microsoft.CodeDom будет проще создавать, так вы программируете боле высокоуровневыми командами. Код созданный на Reflecion.Emit будет сложней создать на IL (к примеру циклы надо будет создавать с помощью переходов), особенно когда вы программируете сложную логику. Из этого следует, что возможностей на Reflection.Emit больше (так у IL больше возможностей, чем у C#). Однако для каждой привычной вещи на C# придётся писать намного больше кода. Производительность Код Microsoft.CodeDom будет медленней, чем Reflection.Emit, так как он компилируется затем генерируется C#-код, затем этот сгенерированный код компилируеся. Reflection.Emit сразу компилируется в сборку. Платформенная (не)зависимость Как верно заметил @Grundy это платформенная независимость для Reflecion.Emit. А Microsoft.CodeDom доступен только на платформах от Microsoft. Roslyn (основано на вопросе) Однако я бы не использовал Microsoft.CodeDom. Ибо Roslyn мощней и выразительней. Roslyn есть на Mono (платформенно-независимый). Roslyn в отличии от Microsoft.CodeDom не просто является обёрткой на csc.exe, а може также анализировать код семантически, динамически компилировать и разбирать код. Это способ создавать любой инстумент для C# и VB. Поэтому есть возможности от форматирования код до нахождения всех ссылок в решении для данного символа. T4 А что если вам не надо динамически генерируемую сборку, а нужно дополнить часть существующего решения? Или если надо динамически сгенерированные файлы, но не C# или VB? К примеру, скаже JavaScript или CSS. (Для HTML рекомендую RazorEngine если что, но с помощью T4 тоже можно). Или вообще какого-то экзотического расширения. И ещё, усугубив задачу, этих файлов надо несколько, но количество известно только в момент исполнения кода. Тогда вы можете использовать T4. Это инструмент в Visual Studio (или просто консольная утилита), который используется для генерации кода любого языка. Также доступен и в MonoDevelop. Сразу скажу, что Entity Framework (не .NET Core) использует T4. Концептуально похоже на ASPX, только код между C#-вставками <# #> не обязательн HTML/CSS/JavaScript, а на языке программирования целевого файла. Мануалы. Из минусов Нет встроенной поддержки подсветки синтаксиса и Intellisense в Visual Studio, поэтому приходится либо привыкать, либо устанавливать третьесторонние плагины для Visual Studio.

Ответ 2



Как уже сказано в предыдущем ответе, существует несколько способов кодогенерации Часть из них позволяют генерировать код в дизайн-тайме, т.е. в IDE (T4). Другая - генерировать C# код во время исполнения (CodeDom), а третья - генерировать исполняемый код (т.е. IL) во время исполнения (Reflection.Emit). И хотя может показаться, что последний случай довольно редкий, но это не так. Даже не задумываясь, вы этим делом пользуетесь постоянно. Компилируемые регулярные выражения Да, если вы создаете регулярное выражение следующим образом: var regex = new Regex("abc" RegexOptions.Compiled);, то среда исполнения сгенерирует класс со специализированным конечным автоматом с помощью Reflection.Emit, а не будет интерпретировать регулярное выражение каждый раз. Сериализаторы Да, большинство сериализаторов используют отражение, но это весьма медленная штука Поэтому тот же XmlSerializer сгенерирует специальный класс сериализатора во время исполнени и сохранит его в специальной папке для последующего использования. Этот же подход используется и бинарным сериализатором, да и любимый всеми Newtonsoft.Json не брезгует кодогенерацией. PostSharp и прочие AOP Например, мы хотим сделать ран-тайм декоратор для логгирования. Если у нас есть виртуальны метод, помеченный атрибутом [LogMethod], то мы можем сгенерировать класс наследника, который переопределит этот виртуальный метод и будет логировать все аргументы метода, а уже потом вызывать базову реализацию. Постшарп и другие AOP фреймворки построены непосредственно на технологии генерации кода во время исполнения. Деревья выражений Да, Expression Trees в C# - это высокоуровневая конструкция, но метод Expression.Compile на самом деле использует Reflection.Emit для генерации кода во время исполнения. Object-to-object mappers Всякие Automapper-ы используют Reflection.Emit для генерации соответствия между отображением экземпляров одного типа в другой. ORM Это частный случай мапперов и в них используется кодогенерация по полной программе NHibernate, LINQ 2 SQL, Entity Fraework, все они генерируют классы во время исполнения для маппинга объектов друг на друга или для перехвата вызовов метода репозиториев. Mock frameworks Rhyno Mocks, Moq и другие, все они основаны либо на Expression Trees либо напряму на Reflection.Emit-е (да, многие mock framework-и используют DynamicProxy, который уже использует Reflection.Emit) для генерации заглушек при тестировании. WCF и прочие коммуникационные штуки Да, и в них тоже генерируются различные прокси классы для инспекции вызовов и другой инструментации во время исполнения. Application Insights и прочие логгеры/анализаторы И снова, для того, чтобы сделать процесс логгирования наиболее простым, в подобных инструментах используется кодогенерация. DLR - Dynamic Languagage Runtime В CLR есть специальная функциональность для взаимодействия с динамически типизированным языками, типа Python, Ruby etc. Там на полную катушку используется DLR для повышения производительности. Вы не поверите, но вы можете использовать Reflection.Emit (хоть и косвенно) даж для обхода стандартных проблем языка C#. Возможно вы знаете, что new T() ограничение для дженериков - это страх и ужас. Та используется отражение, что серьезно сказывается на производительности, да еще и заворачивае исключения, возникшие при создании экземпляра, в TargetInvocationException. Я уже в нескольких проектах сталкивался, что этот метод существенно портит end-to-end время исполнения. Поэтому в этих проектах используется специализированный CustomActivactor: public static class CustomActivator { public static T CreateInstance() where T : new() { return ActivatorImpl.Factory(); } private class ActivatorImpl where T : new() { // Под колпаком используем Reflection.Emit! private static readonly System.Linq.Expressions.Expression> _expr = () => new T(); public static readonly Func Factory = _expr.Compile(); } } Теперь наш активатор не страдает от проблем с производительностью и не оборачивает исключения из конструкторов в занудный TargetInvocationExpression. З.Ы. На самом деле, я коснулся лишь части сценариев, тот же ASP.NET использует кодогенераци по полной. Чтобы понять всю полноту картины можно попробовать поискать самому. Например, можно поискать тип ILGenerator или что-то еще из пространства имен System.Reflection.Emit: На гитхаб: найдено 40К ссылок на ILGenerator (там нет семантического поиска, поэтому многие ссылки будут не валидными, но дадут понять масштаб трагедии). в сорцах МС-а: десятки ссылок и примеров использования, большую часть которых я привел ранее. UPDATE: данная тема вылилась в довольно длинный пост, посвященный тонкостям работ активатора, обобщений и кодогенерации - Dissecting the new() constraint in C#: a perfect example of a leaky abstraction.

Комментариев нет:

Отправить комментарий