Tuesday, December 17, 2013

Использование IronPython в C#

Иногда в программах, написанных в среде .Net, не хватает возможности использования динамических языков: выполнять динамический расчет данных во время запуска, изменять работу программы и т.д. Данная статья будет посвящена Dynamic Language Runtime (DLR) с целью демонстрации задач, которые можно решать с помощью данной среды. В этом материале будут расписаны не динамические возможности языка C#, а способы использования динамических языков на базе языка IronPython (язык Python создан для взаимодействия с CLR средой).
Python - мощный скриптовый язык, имеющий много возможностей, которые одновременно с простотой данного языка делают его все более популярным как для разработки различных исполняемых скриптов, так и для полноценного написания прикладных программ и веб-приложений.
Почему-то многие .Net-разработчики часто не рассматривают возможности использования других языков в своем приложении. Наверное, часть из них предполагает, что использование нескольких языков программирования в одном проекте - плохой признак. Пожалуй, нужно разрушить этот миф.
Начнем из того, что приложения, написанные на таких языках, как C#, VB.NET, легко поддаются декомпиляции. С помощью таких средств, как ILSpy или Reflector, можно полностью декомпилировать только что написанную программу, посмотреть ее исходные коды, внести необходимые изменения по мере надобности и собрать программу обратно. После этого программа будет работать не так, как Вы этого хотели. Это один из самых распространенных способов взлома ПО, написанного на управляемом коде (managed code). Поэтому программы приходится защищать с помощью обфускаторов - программ, которые перемешивают управляемый код, заменяют имя функций и т.д., то есть делают все возможное, чтобы программу было тяжелее восстановить в первоначальном виде. К сожалению, этот метод защиты программ - также ненадежная защита: если ПО, написанное на управляемых языках, захотят взломать злоумышленники, то они это сделают.
Перейдем ко второй причине, почему .Net-разработчики не хотят добавлять другие языки к своей программе, - это сложность сопровождения нескольких языков в приложении. В крупных проектах обычно для разных языков выделяют разные команды специалистов. Для небольших проектов от .Net-разработчика требуется больше квалификации и знаний, что в реальных условиях очень затруднено, так как часто бывает, что на одном проекте работают программисты разной квалификации, а умение одновременно работать с несколькими языками говорит о высокой спецификации разработчика. Использование разных языков в проекте позволяет в некоторых случаях спрятать реализацию и сделать ее более стойкой ко взлому ( использование native-языков, таких как С++, С, Assembler, Pascal и т.д.) либо упростить реализацию с добавлением возможностей динамических языков (Python, Ruby) или разделить реализация для клиент серверных приложений, в которых клиент пишется на одном языке, а сервер - на другом. Так чем же помогает использование Python в программах, написанных на C#? Рассмотрим сначала основы и возможности DLR. Далее будет приведена реализация использования языка IronPython в языке C#.
DLR представляет собой среду выполнения, которая добавляет набор служб для динамических языков в свою среду. Она также упрощает разработку динамических языков, используемых в .NET Framework, и добавляет динамические функции в языки со статической типизацией. Динамические языки могут определить тип объекта во время выполнения, тогда как для языков со статической типизацией, например, C# и Visual Basic, следует указать типы объектов. Примерами динамических языков могут служить PHP, Ruby, Python и другие. Также данная среда позволяет разработчикам языков избежать создания лексических анализаторов, средств синтаксического анализа, семантических анализаторов, генераторов кода и других средств, которые обычно приходится создавать самостоятельно.
Для использования среды DLR язык должен создать деревья выражений, которые представляют код на уровне языка в виде древовидной структуры, вспомогательные процедуры среды выполнения и необязательные динамические объекты, реализующие интерфейс IDynamicMetaObjectProvider. Среда DLR и .NET Framework автоматизируют значительное количество задач анализа и генерации кода. Это позволяет разработчикам языков сконцентрировать свои усилия на уникальных функциях языка. Вот как выглядит архитектура среды DLR:
Среда DLR добавляет в среду CLR ряд служб для лучшей поддержки динамических языков. К этим службам относятся следующие:
  • деревья выражений;
  • кеширование места вызова;
  • взаимодействие динамических объектов.

Использование  в языке C#.
Для того, чтобы начать использовать IronPython в программах, написанных на C#, нужно добавить в программу следующие пространства имен:

using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

Чтобы это сделать, можно загрузить IronPython через NuGet Packages, а затем удалить ненужные библиотеки, которые не будут использованы в проекте. После загрузки будут добавлены такие библиотеки, как IronPython.Wpf, IronPython.SQLite и другие. После того, как программа будет запущена и все будет работать, можно пройтись по сборкам и просто удалить те, которые не используются. Как найти, что сборки не используются, можно посмотреть на рисунке ниже:
Вот как будет выглядеть пример кода с использованием языка Python:
class Program
{
    static void Main(string[] args)
    {
        try
        {
            var engine = Python.CreateEngine();
            var scope = engine.CreateScope();
            var source = engine.CreateScriptSourceFromString("52 / 127.0", SourceCodeKind.SingleStatement);
            source.Execute(scope);
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception.Message);
        }
        Console.ReadLine();
    }
}
Выше представлен самый простой вариант программы для вычисления результата деления числа 52 на 127.0. Вы можете подставить свой вариант выражения и посмотреть, что из этого выйдет. Как вариант, можно сразу загружать Python скрипты с помощью функции
engine.CreateScriptSourceFromFile()
Перепишем приведенный выше пример, чтобы можно было показать, как использовать файлы Python в C#. Для этого создадим файл Calculator.py, в который добавим функции add для суммирования двух чисел.

class Calculator(object):
    def add(self, a, b):
        return a + b

Использование:
static void Main(string[] args)
{
    try
    {
        ScriptEngine engine = Python.CreateEngine();
        ScriptSource source = engine.CreateScriptSourceFromFile(@"D:\CSharp\ConsoleApplicationPython\Calculator.py");
        ScriptScope scope = engine.CreateScope();

        ObjectOperations op = engine.Operations;

        source.Execute(scope); // class object created
        object callculator = scope.GetVariable("Calculator"); // get the class object
        object instance = op.CreateInstance(callculator); // create the instance
        object method = op.GetMember(instance, "add"); // get a method
        int result = (int)op.CreateInstance(method, 4, 5); // call method and get result (9)
        Console.WriteLine(result);
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);
    }
    Console.ReadLine();
}
Как видим, у нас добавились новые возможности использования. Теперь осталось добавить финальный штрих - это переписать приведенную выше реализацию с помощью динамических возможностей языка C#. В данном случае это использование ключевого слова dynamic.  
static void Main(string[] args)
{
    try
    {
        var engine = Python.CreateEngine();
        var source = engine.CreateScriptSourceFromFile(@"D:\CSharp\ConsoleApplicationPython\Calculator.py");
        var scope = engine.CreateScope();
        source.Execute(scope);

        dynamic Calculator = scope.GetVariable("Calculator");
        dynamic calc = Calculator();
        int result = calc.add(4, 5);
        Console.WriteLine(result);
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);
    }
    Console.ReadLine();
}

Итоги
В данной статье рассмотрены возможности использования языка IronPython в программах, написанных на языке C#. Но с приведенного примера не видно реальной возможности упрощения использования динамических возможностей. Поэтому представим себе ситуацию, что Вашей программе нужно иметь дело с математическими выражениями. Вам нужно либо самим писать парсер математических выражений с разбором на лексемы, либо использовать какое-то готовое решение, например, NCalc, или взять встроенные средства для математических операций с Python. Вам не нужно будет разбирать Ваше выражение на лексемы и  вычислять по отдельности. Достаточно будет передать выражение в Python, и он сам все сделает за Вас. Пример с использованием Python для работы с математическими выражениями:
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var source = engine.CreateScriptSourceFromString("from math import *", SourceCodeKind.AutoDetect);
source.Execute(scope);

source = engine.CreateScriptSourceFromString("2*sin (2)", SourceCodeKind.AutoDetect);
Console.WriteLine(source.Execute<float>(scope));
Вместо того, чтобы тянуть NCalc и постоянно ее допиливать, как показала практика, функционала, который доступен на данный момент, бывает недостаточно. Поэтому наслаждаемся мощностью работы IronPython с математическими выражениями. Интересным примером также может служить комбинация языка C# и скриптов Python для рынке Форекс. Использование динамических языков может значительно упростить нам жизнь, если использовать их в нужное время в нужном месте.  
  
Список литературы:

No comments:

Post a Comment