Využití C# Scripting

Jan Holan       23. 1. 2017       C#, .NET, Debugging, Kompilátor       1843 zobrazení

Možná jste se jako já dostali do situace, že jste potřebovali vyzkoušet nějaký kus C# kódu na produkčním prostředí nebo někde jinde, kde prostě není (a není dobré instalovat) Visual Studio a vývojové nástroje. Důvodem může být to, že na produkčním prostředí se kód chová jinak, nebo na vývojovém prostředí nelze nasimulovat stejné podmínky. Tento problém řešíme například tak, že si napíšeme nějakou Consolovku nebo formulářovou aplikaci s testovací tlačítkem apod., a nebo nově využijeme C# Scripting.

C# Scripting

C# Scripting je možnost jak psát scripty v jazyce C#. Vše je uděláno díky .NET Compiler Platform ("Roslyn"), a jako celý Rozlyn, je i C# Scripting opensource. Script, který se píše do souboru s příponou .csx, je tedy zapisován v jazyce C#, je zde ale pár změn oproti klasickému .cs. C# příkazy můžeme psát přímo, nemusí být umístěny ve třídě. Druhou hlavní změnou je to, že v C# scriptu nejsou namespaces. Tyto změny jsou z důvodu, aby se psaní scriptů co možná nejvíce zjednodušilo. Zde vidíme jednoduchý příklad C# a C# Script:

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}
using System;

Console.WriteLine("Hello World!");

C# Interactive

Další součástí C# Scripting je command-line nástroj csi - CSharp Interactive. Jedná se o tzv. REPL (read eval print loop) nástroj, který umožňuje zadávat C# příkazy a rovnou je po odeslání vykonávat. Csi dále umožňuje psát některé speciální vestavěné příkazy jako #help – nápověda, #r – načtení reference assemby, atd. Csi.exe je součástí instalace MSBuild. Spustit ho na windows (csi je napsán v .NET Core, takže je dostupný i na Linux, MacOS) můžeme buď přímo z Developer Command Prompt, nebo z cesty “C:\Program Files (x86)\MSBuild\14.0\Bin\” (pro VS2015).

image

Po spuštění csi, již máme načtené některé reference a některé using importy jako using System a další, a tak můžeme rovnou použít např. třídu Console. Definice těchto referencí a using je uvedena v tzv. Response souboru (pro csi konkrétně C:\Program Files (x86)\MSBuild\14.0\Bin\csi.rsp).

Csi také umožňuje spouštět celé .csx script soubory, spuštěním příkazu #load, nebo uvedením .csx souboru při spouštění csi:
csi.exe script-file.csx

Visual Studio C# Interactive Window

Součástí Visual Studia (od VS2015 Update 1) máme nové C# Interactive window, které obsahuje to samé REPL prostředí jako csi. Díky tomu, že je ale postaveno na Visual Studio editoru, tak obsahuje integrovaný IntelliSense, syntax-coloring atd., podobně jako editor .csx souborů.

image

Pokud máme ve VS otevřený .csx script, můžeme jeho příkazy spouštět v Interactive window, pomoci volby Execure in interactive (Ctrl+E, Ctrl+E).

Více o C# Interactive si můžete prostudovat např. zde:
https://github.com/dotnet/roslyn/wiki/C%23-Interactive-Walkthrough
http://dailydotnettips.com/2016/01/12/use-c-interactive-window-for-your-coding-experiment-in-visual-studio-2015/
https://msdn.microsoft.com/en-us/magazine/mt614271.aspx

Spuštění C# scriptu

Zpátky ale k našemu využití tj. máme napsaný C# script s testovacím kódem a potřebujeme ho spustit na nějakém jiném než vývojovém prostředí, kde nemáme VS ani C# Interactive (csi) nainstalován.

Jedna z možností je ručně si nahrát na tento počítač již zmíněný tool csi a script spustit pomoci něho. Jediná prerequisita je plný .NET Framework alespoň v 4.6 nebo vyšší. Potom stačí do nějakého adresáře nahrát následující soubory z vývojového počítače z “C:\Program Files (x86)\MSBuild\14.0\Bin\”:

csi.exe
csi.rsp
Microsoft.CodeAnalysis.CSharp.dll
Microsoft.CodeAnalysis.CSharp.Scripting.dll
Microsoft.CodeAnalysis.dll
Microsoft.CodeAnalysis.Scripting.dll
System.AppContext.dll
System.Collections.Immutable.dll
System.IO.FileSystem.dll
System.IO.FileSystem.Primitives.dll
System.Reflection.Metadata.dll

a spustit csi.exe.

C# Scripting API

Druhou možností je využít C# Scripting API. C# Scripting má totiž ještě jednu součást, o které zatím nebyla řeč, a tou je programové Hosting API. To nám umožňuje hostovat C# script engine a přes něj z našeho programu spouštět kusy C# Script kódu.

Pro použití potřebujeme nainstalovat NuGet balíček Microsoft.CodeAnalysis.CSharp.Scripting:
Install-Package Microsoft.CodeAnalysis.CSharp.Scripting

Dále použijeme using Microsoft.CodeAnalysis.CSharp.Scripting, ve kterém je umístěna třída CSharpScript. Pomoci ní již můžeme vyhodnocovat C# výrazy (EvaluateAsync), nebo zkompilovat a spustit C# script (RunAsync). Příklady jsou uvedeny na stránce Scripting API Samples.

S využitím C# Scripting API jsem si napsal vlastní jednoduchou WPF aplikaci, která umožnuje spustit libovolný C# Script kód.

image

Hlavní kód aplikace je obsluha tlačítka Run pro spuštění scriptu a odchycení výsledků z console do textboxu txtConsole. Kód je umístěn v souboru MainWindow.cs:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Text;
using System.IO;
using Microsoft.CodeAnalysis.CSharp.Scripting;

namespace CSharpScripting
{
    #region ControlWriter
    public class ControlWriter : TextWriter
    {
        private readonly TextBox textbox;

        public ControlWriter(TextBox textbox)
        {
            this.textbox = textbox;
        }

        public override void Write(char value)
        {
            textbox.Text += value;
        }

        public override void Write(string value)
        {
            textbox.Text += value;
        }

        public override Encoding Encoding
        {
            get { return Encoding.ASCII; }
        }
    }
    #endregion

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Console.SetOut(new ControlWriter(txtConsole));

            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            System.Windows.Input.Keyboard.Focus(txtScript);
        }

        private async void ButtonRun_Click(object sender, RoutedEventArgs e)
        {
            ButtonRun.IsEnabled = false;
            this.Cursor = System.Windows.Input.Cursors.Wait;

            try
            {
                txtConsole.Text = "";

                var scriptRunner = CSharpScript.Create(txtScript.Text, GetScriptOptions()).CreateDelegate();
                await scriptRunner.Invoke();
            }
            catch (Microsoft.CodeAnalysis.Scripting.CompilationErrorException ex)
            {
                txtConsole.Text = txtConsole.Text + (txtConsole.Text.Length == 0 ? "" : Environment.NewLine) + string.Join(Environment.NewLine, ex.Diagnostics);
            }
            catch (Exception ex)
            {
                txtConsole.Text = txtConsole.Text + (txtConsole.Text.Length == 0 ? "" : Environment.NewLine) + string.Join(Environment.NewLine, ex.ToString());
            }
            finally
            {
                ButtonRun.IsEnabled = true;
                this.Cursor = null;
            }
        }

        private Microsoft.CodeAnalysis.Scripting.ScriptOptions GetScriptOptions()
        {
            return Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default
                .WithReferences(typeof(System.Diagnostics.Process).Assembly,            //System Assembly
                                typeof(System.Dynamic.DynamicObject).Assembly,          //System.Core Assembly
                                typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly) //Microsoft.CSharp Assembly
                .WithImports(new[] {
                    "System",
                    "System.IO",
                    "System.Collections.Generic",
                    "System.Diagnostics",
                    "System.Dynamic",
                    "System.Linq",
                    "System.Linq.Expressions",
                    "System.Text",
                    "System.Threading.Tasks"
                });
        }
    }
}

Povšimněte si funkce GetScriptOptions, která se stará o to, aby vrátila nastavení pro CSharpScript.Create. Zde řeším přidání některých referencí a using importů, aby byly automaticky pro script k dispozici (podobně jako v csi nebo VS C# Interactive).

Zdrojové soubory celé aplikace i zkompilovanou release verzi naleznete na GitHubu holajan/CSharpScripting.


Jako zdroj zabývající se C# scripting dále doporučuji první část přednášky přímo od jednoho z autorů C# scriptingu:
DotNetCast - Tomáš Matoušek: Interactive Development with C# (v CZ).

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback