ГЛАВНАЯ     АРХИВ     НАПИСАТЬ АДМИНУ     ПОДПИСАТЬСЯ НА RSS     ВОЙТИ      

Поиск

Категории

Облако тегов

  << Предыдущий пост       Следующий пост >>  
17. мая 2011 12:11

Оригинал статьи: Скрипты в NET(Mono) средствами самой платформы

Автор статьи (на хабре): zebraxxl

 

Введение

Работая тут над одним проектом, потребовалось мне, что бы функциональность проекта расширялась на лету и сторонними разработчиками, причём возможностей к расширению было как можно больше, с возможностью правки кода на лету. Соответственно плагины для этого не очень годились из-за необходимости их постоянной перекомпиляции после любой правки. Выход: скрипты. До этого со скриптами я работал достаточно давно и это была Lua на C++. Вариант хороший, если бы не несколько минусов:

  • Отсутствие нормальной реализации или прослойки под .NET/Mono — во всех что я видел были какие-то недоработки (может и плохо искал — как минимум пропустил Lua в TaoFramework)
  • По всей видимости, нужно было писать кучу биндингов что бы среда исполнения .NET/Mono могла нормально взаимодействовать со средой Lua и обратно.

Тогда-то я и задался вопросом — а может быть в .NET/Mono уже есть что-нибудь для реализации скриптов? И ответ был да. Пространство имён "System.CodeDom.Compiler" было как раз то, что мне надо — возможность реализовать скрипты которые максимально соединялись со средой .NET/Mono.Правда если посмотреть на мой механизм скриптов изнутри, то это получается и не скрипты даже, а код, написанный на C#, просто динамически компилируется и загружается в память для выполнения. Однако, не смотря даже на такой «фейк», результата я добился — я мог править код, на лету перекомпилируя его прямо в своём приложении. И при этом это будет работать даже на машинах где не установлены Visual Studio и другие средства разработки, так как компиляторы, как минимум, C# и VB.NET идут прямо вместе с .NET и Mono.
Пример, о котором говориться в статье, можно скачать тут: http://zebraxxl.programist.ru/ScriptsInDotNet.zip

Пространство имён "System.CodeDom.Compiler"

Собственно самое главное в данной статье. Именно здесь собраны классы для компиляции кода в .NET/Mono сборки. Основным классом выполняющим работу является класс CodeDomProvider. По своей сути фактически — это интерфейс над компилятором, который просто подготавливает данные для компиляции и вызывает компилятор выбранного языка с нужными параметрами. Ну начнём по порядку.

А какие языки у нас вообще поддерживаются?

Что бы это выяснить достаточно вызвать статический метод CodeDomProvider.GetAllCompilerInfo. Данный метод вернёт массив СompilerInfo. В теории конечно может быть и так, что один компилятор поддерживает сразу несколько языков, но на практике я пока с таким не встречался. Но как раз на случай «многоязычности» компилятора в .NET/Mono как раз и сделано так, что сначала мы получаем информацию об отдельных компиляторах, а уже потом смотрим кто, какой язык поддерживает. Вот примеры вывода этой информации из примера:

0 compiler languages:
c#
cs
csharp
1 compiler languages:
vb
vbs
visualbasic
vbscript
2 compiler languages:
js
jscript
javascript
3 compiler languages:
vj#
vjs
vjsharp
4 compiler languages:
c++
mc
cpp

Как здесь может быть видно — фактически у меня в распоряжении имеется 5 языков: C#, VB.NET, J#, JScript, C++. Так же вывод показывает что каждым компилятором обслуживается один язык.
Здесь можно ещё добавить что на других компьютерах (без установленной Visual Studio — машина моего друга, который к IT не имеет никакого отношения и моём сервере Ubuntu server 11.04, Mono из репозитария (2.6.7)) выдаёт такой же результат.
А что нам вообще даёт поддержка множества языков? А даёт нам это то, что мы сможем писать скрипты на различных языках и при этом совершенно не задумываясь об их обработке их в приложении. А это в свою очередь повышает уровень вхождения сторонних разработчиков.

А теперь она! Компиляция

Ну а теперь попробуем скомпилировать что-нибудь. Напишем какой-нибудь тестовый класс на C#. Его можно найти в файле «TestScript.cs». Сам по себе класс достаточно простой — всего два метода. Один статический, другой нет. Оба просто выводят текст в консоль и возвращают строку. Так же в статический передаётся один параметр.
Для начала компиляции нам надо получить чем компилировать. Для этого выполняем статический метод "CodeDomProvider.CreateProvider". В качестве параметра он принимает название языка, на котором компилируем. Сами названия мы получали на предыдущем шаге.
Следующим шагом будем заполнять параметры компиляции. Все параметры хранятся в классе "CompilerParameters". Наиболее интересные поля:

  • CompilerOptions — дополнительные параметры компилятору (т.е. что будет передваться, к примеру, csc в случае с C#)
  • GenerateExecutable — генерировать сборку в исполняемом файле. Вообще по факту он в любом случае будет сделан. Только если это поле false — то исполняемый файл будет сгенерирован во временной папке. А если true — то он окажется там, где скажет поле "OutputAssembly"
  • GenerateInMemory — генерировать сборку в памяти. Опять же по факту сначала сборка будет в исполняемом файле, откуда будет загружаться.
  • IncludeDebugInformation — очень полезное поле. Если записать сюда true, то будет сгенерированная отладочная информация и можно будет отлаживать скрипты прямо параллельно с основным приложением.
  • ReferencedAssemblies — какие сборки использовать при компиляции. Тоже нужное поле. В моём проекте я записывал сюда имя основного приложения что бы скрипты имели доступ его структуре и могли полноценно интегрироваться с приложением.


Ну а теперь все просто — взываем CodeDomProvider.CompileAssemblyFrom* в зависимости от того, откуда получаем исходник. Тут три варианта:

  • CompileAssemblyFromFile — из файла. Последний параметр — список имён файлов для компиляции
  • CompileAssemblyFromSource — из строк. Подразумевается что мы уже прочитали исходные файлы и они у нас хранятся в массиве строк в последнем параметре
  • CompileAssemblyFromDom — из DOM дерева исходных кодов. Что-то типа DOM для html (или xml) документа


Первый параметр во всех этих методах — параметры "CompilerParameters". В качестве результата получаем "CompilerResults". Отсюда можно получить всю информацию о том, как прошла компиляция. Самые интересные и нужные поля "CompilerResults":

  • CompiledAssembly — полученная сборка загруженная в память (если было установлено поле GenerateInMemory
  • Errors — ошибки и предупреждения которые выдал компилятор


Итак если CompiledAssembly не null (то есть сборка скомпилировалась успешно) то имеется мы уже можем пользоваться нашим скриптом как полноценной сборкой .NET/Mono получая доступ к необходимым нам функциям и полям через рефлексию. Я не буду останавливаться на этой части более подробно — всё таки это уже другая тема.

А скрипты попроще?

Это конечно хорошо использование такого мощного языка в качестве скриптов — это открывает огромные возможности для расширения функционала приложения, но в некоторых случаях это может оказаться забиванием гвоздей микроскопом. Разница в двух вариантах сложности кода очевидна:

class="cs">class="keyword">using System;

class="keyword">namespace Script
{
class="keyword">public class="keyword">class Script
{
class="keyword">public class="keyword">static class="keyword">void M()
{
Console.WriteLine(“Hello world”);
}
}
}

Или такой вариант:

class="cs">WriteLine(“Hello World”);

Два кода которые в принципе делают одно и то же (при условии что во втором варианте под WriteLine понимается вызов System.Console.WriteLine) — но как говориться: «Почувствуйте разницу».
Способов добиться такого упрощения можно придумать множество. И у каждого из них будут свои преимущества и недостатки. Однако все они будут сводиться к одному: к динамической генерации «большого» кода из «маленького». Для примера я взял самый простой способ, который смог придумать:

  1. Создал отдельный класс (далее класс-реализация) который содержит в себе все методы, готорые в скриптах являются глобальными (в примере — класс MiniScriptWorker).
  2. Создал шаблон для генерации «большого» кода — один дочерний от класса-реализации класс с одним пустым методом (файл «ScriptTemplate.cs»)
  3. «Маленький» код просто вставляется в реализацию того самого пустого метода класса-шаблона (файл «MiniScript.cs»)


Заключение

Вот таким несложным путём мы получаем достаточно мощные скрипты практически бесплатно в .NET/Mono приложении без необходимости тянуть за собой дополнительные зависимости. Да — в некоторых случаях такой способ наверно будет даже избыточен, но всё зависит всё-таки от задачи — в моём случае именно такая реализация была самой лучшей и удобной.
Ещё раз о плюсах такого способа: в качестве скриптов у вас будет мощный язык с практически полным доступом к среде .NET/Mono (насколько полный решать вам — какие сборки будете добавлять во время компиляции, то и будет доступно скриптам). Так же, просто добавив своё приложение в список сборок для скрипта, вы получите практически полную интеграцию с вашим приложением и возможность видоизменять его так, как вам, или разработчику скрипта, захочется.
Однако из этого растут и минусы. Иногда может потребоваться скрыть какой-то кусоск основного приложения от скрипта. От этого можно защитится путём сокрытия таких частей в область видимости "internal". Правда эту защиту можно обойти при помощи другой проблемы — из-за такого широкого доступа к среде .NET/Mono страдает безопасность. К примеру нам ничего не сможет помешать используя System.IO.FileStream открыть какой-нибудь файл с паролями и потом спокойно отослать его содержимое куда-нибудь в чужие руки. К сожалению решения данной проблемы у меня пока нет — не успел разобрать вопрос более подробно.

Похожие записи


Шорт С. - Разработка XML Web-сервисов средствами Microsoft .NET
Название: Разработка XML Web-сервисов средствами Microsoft .NET Автор: Шорт С. Издательство: СПб.: БХВ-Петербург Серия или Выпуск: Мастер программ ISBN: 5-94157-251-4 Год: 2003 Страниц: 480 От издателя: Книга Скотта Шорта посвящена построению и применению Web-сервисов средствами платформы Microsoft .NET. Подробно рассматриваются интернет-технологии ASP.NET и ...

Telerik RadControls for Silverlight 4 – готовые компоненты бизнес-приложений для новой платформы
Оригинал статьи находится здесь. Компания Telerik выпустила новую версию своего набора готовых компонентов для создания приложений на платформе Microsoft Silverlight 4. Среди прочих нововведений стоит выделить появление нового компонента RichTextBox для ввода и отображения форматированного текста, поддержку среды разработки Microsoft Visual Studio 2010, поддержку...

Visual Studio в рамках технологической платформы Microsoft
Visual Studio предоставляет возможности не только для создания, отладки и тестирования приложений, но и обеспечивает инфраструктуру командной разработки, которая позволяет организовывать функционирование команд разнообразного размера и по самым различным методологиям разработки: начиная с Agile (Scrum и прочие), заканчивая формальными – CMMI 3 и выше. Существенн...

Добавить комментарий




biuquote
  • Комментарий
  • Предпросмотр
Loading


  Сохранить комментарий