Несколько лет назад, я заметил, что в Visual Studio появилась новая окошко “C# Interactive”. Eщё тогда я стал догадываться, что это REPL, однако только недавно решил разобраться, что оно из себя представляет и в каких случаях может быть полезно. Прежде чем мы перейдём, к “С# Interactive” давайте кратко обсудим, что же такое REPL
Что такое REPL?

REPL или “read-eval-print loop” - это интерактивная среда разработки (в командной строке), в которой пользователь вводит выражение на языке программирования, а REPL, в свою очередь, считывает (Read) его, выполняет (Eval) и отображает результат (Print), а затем этот процесс снова повторяется (Loop). Всё просто, не так ли? А чтобы было ещё проще вот небольшой пример.

Аббревиатура REPL сложилась из названий функций языка LISP, с помощью которых REPL собственно и был реализован. Примерно вот так выглядит REPL на языке LISP:
(loop (print (eval (read))))
REPL в C# появился вместе с Roslyn и благодаря ему, так как Rolsyn изначально разрабатывался с целью предоставления разработчикам API для работы с компилятором. Подробнее об истории появления Rolsyn и о способах его использования можно почитать здесь. Там же в данной статье вы можете найти упрощенный способ реализации REPL на C#.
class Program
{
static void Main(string[] args)
{
ScriptState<object> scriptState = null;
while (true)
{
Console.Write("* ");
var input = Console.ReadLine();
scriptState = scriptState == null ?
CSharpScript.RunAsync(input, ScriptOptions.Default.AddImports("System")).Result :
scriptState.ContinueWithAsync(input).Result;
}
Console.ReadLine();
}
}
Для чего нужен REPL?
Теперь вы знаете, что такое REPL, но я думаю, сейчас в вашей голове примерно следующие мысли “Окей, REPL, круто, а зачем?“. И это самый главный вопрос, ведь каждый инструмент должен решать какие-то проблемы. В нашем случае, REPL позволяет нам мгновенно увидеть результат выполнения кода, без ожидания его компиляции. К тому же вам не нужно создавать новый проект, чтобы протестировать там пару строк кода (У меня целое кладбище таких проектов).
Вы можете использовать REPL, чтобы ознакомиться или поиграться с API, если не уверены в том, как он работает. Давайте наконец-то откроем Visual Studio (если вы используете Rider, то там тоже есть окно “C# Interactive”) и запустим REPL, его можно найти по пути “View > Other Windows > C# Interactive”.

Теперь давайте представим, что мы забыли, мутирует ли объект метод Concat
типа List<int>
. Тут нам и поможет REPL.

Как видно из примера, метод Contact
не мутирует существующий список, а возвращает новый, в котором содержатся элементы списков list1
, list2
. Необходимо подчеркнуть тот факт, что для того чтобы увидеть результат выполнения выражения, нужно чтобы оно не оканчивалось точкой с запятой, иначе результат выведен не будет. Как вы могли заметить, в C# Interactive работает привычный нам IntelliSense, что гораздо упрощает работу.
К счастью, это не все возможности C# Interactive, вы можете использовать его также для изучения сторонних библиотек. Однако, он не позволяет напрямую работать с менеджером пакетов Nuget, поэтому необходимо предварительно скачать нужную вам библиотеку или, если вам необходимо изучить библиотеку которая используется в вашем проекте, можно просто указать путь к ней следующим образом:
#r "...\path\library.dll"
Давайте посмотрим как можно использовать C# Interactive для изучения API библиотеки Octokit.

Также нужно отметить, что вам не обязательно запускать Visual Studio, чтобы использовать REPL. Вы можете запустить Developer Command Prompt for VS и выполнить команду csi
.

C# Repl доступен не только на Windows. На Linux и Mac может использоваться Mono C# Repl.
DotNet Script
C# Interactive является не единственным REPL окружением для C#, существует ещё минимум 2:
Данные окружения позволяют работать с менеджером пакетов Nuget. Но в данной статье я рассмотрю только DotNet-Script.
Давайте запустим тот же самый пример с библиотекой Octokit, используя DotNet Script (об установке dotnet script можно почитать здесь).

Для запуска dotnet script необходимо запустить в консоли dotnet script
или dotnet-script
. Как вы уже заметили из примера, для подключения библиотеки из Nuget используется следующая запись: #r "nuget: <library>, <version>"
, в нашем случае - это #r "nuget: Octokit, 0.48.0"
. Первый запуск команды займет некоторое время, так как будет происходить скачивание библиотеки, но в дальнейшем, запуск команды будет занимать гораздо меньше времени, так как библиотека будет закэширована.
DotNet Script использует .net core и поэтому является кросплатформенным, благодаря чему вы можете запустить его на любой платформе.
Однако, чтобы получить intellisense для dotnet script вам необходимо использовать текстовый редактор, поддерживающий OmniSharp. Я покажу как это сделать на примере уже знакомого нам, примера с Octokit в Visual Studio Code. Для начала нам нужно инициализировать новый проект dotnet script с помощью команды dotnet script init
.

Затем необходимо установить C# Extension для Visual Studio Code. После чего необходимо вставить уже знакомый нам пример в файл main.csx
.
#!/usr/bin/env dotnet-script
#r "nuget: Octokit, 0.48.0"
using Octokit;
var github = new GitHubClient(new ProductHeaderValue("CherkashinBlogPost"));
var user = await github.User.Get("acherkashin");
Console.WriteLine($"{user.Followers} folks follow acherkashin");
Console.WriteLine(user.Blog);
var pullRequests = await github.PullRequest.GetAllForRepository("storybookjs", "storybook");
Console.WriteLine(pullRequests.Count());
var lastPR = pullRequests.Where(x => x.CreatedAt > DateTimeOffset.Now.Subtract(TimeSpan.FromDays(2)));
foreach(var q in lastPR) {
Console.WriteLine($"{q.CreatedAt}...{q.User.Login}...{q.Title}");
}
Как вы можете увидеть из скриншота ниже, intellisense в VsCode работает также хорошо как, и в Visual Studio.

Итак, в начале я сказал, что вам не нужно создавать пустой проект, для того, чтобы протестировать какой-то кусок кода или поиграться с API библиотеки, а в итоге делаю всё с точность до наоборот. Дело в том, что dotnet script представляет собой не только REPL, но и полноценный скриптовый язык. Теперь вы можете писать скрипты на своём любимом языке 😉 и забыть о изучении bash, python, PowerShell… Подробнее об этом вы можете узнать в статье Hitchhiker’s Guide to the C# scripting.
Напоследок хочется отметить, что dotnet scripts также предоставляет возможность отладки. Таким образом вы сможете не только писать скрипты на C#, но и удобно отлаживать их.

Если вам понравилась данная тема, то вы можете подробнее познакомиться с dotnet scripts в следующей статье C# REPL for .NET Core 2.0 and #load support from Nuget – dotnet-script 0.16 is out.