[.NET, C#] Скриптинг в C# или динамическое выполнение в runtime
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет Хабр!Думаю, не многие знают, что в C# есть штука на подобии eval из других языков. Благодаря Rosyln API, можно во время выполнения скомпилировать и выполнить C# код. Пример использования можете посмотреть в моей реализации REPL-а для C#.Впервые с такой штукой, как REPL, я познакомился когда теребил питона. В мире .NET есть похожая вещь под названием C# Interactive (CSI). Довольно удобная штука, но у нее есть один большой минус — она входит в состав инструментов Visual Studio, так что без установки VS, не получится ее использовать, а чтобы запускать ее без запуска VS, вообще надо лезть в ее недра (а точнее, через консоль запустить C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat), что так себе.Есть еще такие проекты, как dotnet-script и cs-script (они работают через Microsoft.CodeAnalysis.CSharp.Scripting), но у них есть фатальный недостаток — они написаны не мной. Вот и появилась мысль, написать свой корявый велосипед, но со своими фичами! (которые тоже коряво работают). После недолгих поисков, мой взор упал на сие чудо: Microsoft.CodeAnalysis.CSharp.Scripting. Из плюсов — удобный API, возможность выполнять код без классов и namespace-ов.Для начала, нужно поставить нугет пакет Microsoft.CodeAnalysis.CSharp.Scripting и сделать using
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
CSharpScript - статичный класс, который поможет нам создать скрипт, включает 3 метода:
- Create - создает Script с указанным кодом и параметрами, который можно будет в последствии скомпилировать и запустить
- RunAsync - который компилирует, выполняет переданный код и возвращает ScriptState
- EvaluateAsync - выполняет код и возвращает результат выполнения
CSharpScript.Create можно использовать, когда вам нужно предварительно скомпилировать скрипт и часто вызывать его.
var script = CSharpScript.Create("System.Console.WriteLine("Hello from script")");
script.Compile();
await script.RunAsync();
Eсли не вызвать Compile(), то код будет скомпилирован при первом вызове. Для удобства можно создать ScriptOptions, в котором можно будет добавить namespace-ы и reference-ы (можно также добавить статические классы, на подобии using static).
var options = ScriptOptions.Default
.AddImports("System", "System.IO", "System.Collections.Generic",
"System.Console", "System.Diagnostics", "System.Dynamic",
"System.Linq", "System.Text",
"System.Threading.Tasks")
.AddReferences("System", "System.Core", "Microsoft.CSharp");
CSharpScript.Create("Console.WriteLine("Hello from script")", options);
Но здесь есть один момент — ScriptOptions почему-то не ограничивают доступные namespace-ы. Этакий whitelist, как я изначально подумал, возможно, просто не до конца разобрался.CSharpScript.RunAsync возвращает ScriptState, его можно дополнить вызвав ContinueWithAsync, который скомпилирует, выполнит код и вернет новый объект ScriptState. Можно повторно запустить скрипт, обратившись к свойству Script. Для получения результата, есть свойство ReturnValue.
ScriptState state = await CSharpScript.RunAsync("int x = 5;");
state = await state.ContinueWithAsync<int>("x + 1");
Console.WriteLine(state.ReturnValue); // 6
У объекта state можно посмотреть объявленные переменные, а так же полученный exception
foreach(var variable in state.Variables)
{
Console.WriteLine($"{variable.Name} - {variable.Value}");
}
С помощью CSharpScript.Create можно создать делегат из скрипта, который будет запускать скрипт при вызове
var script = CSharpScript.Create<Func<int,int>>("x => x+1");
Console.WriteLine(await script.CreateDelegate().Invoke(1)); // 2
А так же, можно скомпилировать лямбда-выражение в виде строки, используя CSharpScript.EvaluateAsync (или другими способами, которые были выше)
var deleg = await CSharpScript.EvaluateAsync<Func<int, int>>("x => x * 2");
Console.WriteLine(deleg(5)); // 10
Это может быть удобно для сериализации и десериализации лямбда-выражений (Мой скудный ум не смог придумать юзкейса для этого, но я встречал людей, которым такая штука была нужна)Ниже представлены тесты:
Не думаю, что они очень точны, но помогут примерно понять скорость выполнения. С кодом можете ознакомится по ссылке.ЗаключениеMicrosoft.CodeAnalysis.CSharp.Scripting, довольно удобная шутка, для runtime выполнения C# кода. Можно использовать например в своем движке, или для предоставления способа модификации, без надобности создания .net проекта и его сборки.Топ 5 популярных реп в github, которые используют данный пакет:
Дополнительные примеры можно найти по ссылкам внизу:https://github.com/dotnet/roslyn/blob/main/docs/wiki/Scripting-API-Samples.mdhttps://github.com/dotnet/roslyn/tree/a7319e2bc8cac34c34527031e6204d383d29d4ab/src/ScriptingНадеюсь, моя первая статья не показалось слишком скучной, и я смог как-то вам помочь.Хорошего вам дня!
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, Программирование, .NET, Тестирование веб-сервисов] Как я решил протестировать нагрузочную способность web сервера
- [.NET, C#] Добавляем CRUD в ASP.NET Core проект за 10 минут с помощью EasyData
- [Разработка игр, C#, Unity] Судно на воздушной подушке на Unity 3D
- [Совершенный код, C#] Комментарии — ложь
- [Разработка игр, C#, Unity] Регдоллы на Unity 3D
- [.NET, C#] Pure DI для .NET
- [.NET] Описание элементов перечислений в Swashbuckle (перевод)
- [Разработка игр, C#, Unity] Разработка своей Just Shapes & Beats. Канвас и немного об оптимизации
- [Программирование, .NET, C#] Cоздание переиспользуемых Linq фильтров (построителей предикатов для Where), которые можно применять для разных типов
- [IT-инфраструктура, Service Desk, Управление проектами, Карьера в IT-индустрии] Как мы развернули круглосуточную техническую поддержку с нуля всего за 1 год
Теги для поиска: #_.net, #_c#, #_.net, #_.net_core, #_c#, #_eval, #_roslyn, #_.net, #_c#
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:59
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет Хабр!Думаю, не многие знают, что в C# есть штука на подобии eval из других языков. Благодаря Rosyln API, можно во время выполнения скомпилировать и выполнить C# код. Пример использования можете посмотреть в моей реализации REPL-а для C#.Впервые с такой штукой, как REPL, я познакомился когда теребил питона. В мире .NET есть похожая вещь под названием C# Interactive (CSI). Довольно удобная штука, но у нее есть один большой минус — она входит в состав инструментов Visual Studio, так что без установки VS, не получится ее использовать, а чтобы запускать ее без запуска VS, вообще надо лезть в ее недра (а точнее, через консоль запустить C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat), что так себе.Есть еще такие проекты, как dotnet-script и cs-script (они работают через Microsoft.CodeAnalysis.CSharp.Scripting), но у них есть фатальный недостаток — они написаны не мной. Вот и появилась мысль, написать свой корявый велосипед, но со своими фичами! (которые тоже коряво работают). После недолгих поисков, мой взор упал на сие чудо: Microsoft.CodeAnalysis.CSharp.Scripting. Из плюсов — удобный API, возможность выполнять код без классов и namespace-ов.Для начала, нужно поставить нугет пакет Microsoft.CodeAnalysis.CSharp.Scripting и сделать using using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
var script = CSharpScript.Create("System.Console.WriteLine("Hello from script")");
script.Compile(); await script.RunAsync(); var options = ScriptOptions.Default
.AddImports("System", "System.IO", "System.Collections.Generic", "System.Console", "System.Diagnostics", "System.Dynamic", "System.Linq", "System.Text", "System.Threading.Tasks") .AddReferences("System", "System.Core", "Microsoft.CSharp"); CSharpScript.Create("Console.WriteLine("Hello from script")", options); ScriptState state = await CSharpScript.RunAsync("int x = 5;");
state = await state.ContinueWithAsync<int>("x + 1"); Console.WriteLine(state.ReturnValue); // 6 foreach(var variable in state.Variables)
{ Console.WriteLine($"{variable.Name} - {variable.Value}"); } var script = CSharpScript.Create<Func<int,int>>("x => x+1");
Console.WriteLine(await script.CreateDelegate().Invoke(1)); // 2 var deleg = await CSharpScript.EvaluateAsync<Func<int, int>>("x => x * 2");
Console.WriteLine(deleg(5)); // 10 Не думаю, что они очень точны, но помогут примерно понять скорость выполнения. С кодом можете ознакомится по ссылке.ЗаключениеMicrosoft.CodeAnalysis.CSharp.Scripting, довольно удобная шутка, для runtime выполнения C# кода. Можно использовать например в своем движке, или для предоставления способа модификации, без надобности создания .net проекта и его сборки.Топ 5 популярных реп в github, которые используют данный пакет: Дополнительные примеры можно найти по ссылкам внизу:https://github.com/dotnet/roslyn/blob/main/docs/wiki/Scripting-API-Samples.mdhttps://github.com/dotnet/roslyn/tree/a7319e2bc8cac34c34527031e6204d383d29d4ab/src/ScriptingНадеюсь, моя первая статья не показалось слишком скучной, и я смог как-то вам помочь.Хорошего вам дня! =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:59
Часовой пояс: UTC + 5