Januar 2016

Band 31, Nummer 1

.NET-Grundlagen – C#-Skripterstellung

Von Mark Michaelis | Januar 2016

Mark MichaelisMit der Veröffentlichung von Visual Studio 2015 Update 1, im weiteren Verlauf als Update 1 bezeichnet, wird eine neue C#-REPL (Read-Evaluate-Print-Loop) eingeführt, die in Visual Studio 2015 als neues interaktives Fenster oder als neue Befehlszeilenoberfläche (CLI, Command-Line Interface) mit dem Namen CSI verfügbar ist. Nicht nur erschließt Update 1 der Sprache C# die Befehlszeile, es wird auch eine neue C#-Skriptsprache eingeführt, die traditionell in einer CSX-Datei gespeichert wird.

Bevor wir in die Details der neuen C#-Skripterstellung einsteigen, muss ein Verständnis für die Zielszenarien erworben werden. C#-Skripterstellung ist ein Tool zum Testen von C#- und .NET-Codeausschnitten, ohne den Aufwand mehrerer Komponententest- oder Konsolenprojekte. Sie stellt eine schlanke Option bereit, mit der schnell auf der Befehlszeile ein LINQ-Aggregatmethodenaufruf erstellt werden, die .NET API für das Entzippen von Dateien aktiviert oder eine REST-API aufgerufen werden kann, um deren Rückgabewerte oder Arbeitsweise zu ermitteln. Damit steht ein einfaches Verfahren zur Verfügung, um APIs zu erforschen und zu verstehen, ohne den Mehraufwand noch einer weiteren CSPROJ-Datei in Ihrem %TEMP%-Verzeichnis.

Die C#-REPL-Befehlszeilenoberfläche (CSI.EXE)

Wie schon beim Lernen der Sprache C# selbst ist die beste Methode für den Einstieg in die C#-REPL-Oberfläche einfach, sie zu starten und mit dem Ausführen von Befehlen zu beginnen. Führen Sie zum Starten den Befehl „csi.exe“ auf der Visual Studio 2015-Entwicklereingabeaufforderung aus, oder geben Sie den vollständigen Pfad ein, „C:\Programme (x86)\MSBuild\14.0\bin\csi.exe“. Beginnen Sie von dort aus mit der Ausführung von C#-Anweisungen, wie in Abbildung 1 gezeigt.

Abbildung 1 CSI-REPL-Beispiel

C:\Program Files (x86)\Microsoft Visual Studio 14.0>csi
Microsoft (R) Visual C# Interactive Compiler version 1.1.0.51014
Copyright (C) Microsoft Corporation. All rights reserved.
Type "#help" for more information.
> System.Console.WriteLine("Hello! My name is Inigo Montoya");
Hello! My name is Inigo Montoya
> 
> ConsoleColor originalConsoleColor  = Console.ForegroundColor;
> try{
.  Console.ForegroundColor = ConsoleColor.Red;
.  Console.WriteLine("You killed my father. Prepare to die.");
. }
. finally
. {
.  Console.ForegroundColor = originalConsoleColor;
. }
You killed my father. Prepare to die.
> IEnumerable<Process> processes = Process.GetProcesses();
> using System.Collections.Generic;
> processes.Where(process => process.ProcessName.StartsWith("c") ).
.  Select(process => process.ProcessName ).Distinct()
DistinctIterator { "chrome", "csi", "cmd", "conhost", "csrss" }
> processes.First(process => process.ProcessName == "csi" ).MainModule.FileName
"C:\\Program Files (x86)\\MSBuild\\14.0\\bin\\csi.exe"
> $"The current directory is { Environment.CurrentDirectory }."
"The current directory is C:\\Program Files (x86)\\Microsoft Visual Studio 14.0."
>

Als erstes fällt das Offensichtliche ins Auge – es ist wie C# – wenn auch ein neuer Dialekt von C# (aber ohne das zeremonielle Drumherum, das in einem vollständigen Produktionsprogramm sinnvoll und in einem schnell hingeworfenen Prototyp überflüssig ist). Daher können Sie, wie erwartet, zum Aufrufen einer statischen Methode den vollqualifizierten Methodennamen niederschreiben und Argumente in Klammern übergeben. Wie in C# deklarieren Sie eine Variable, indem Sie ihr den Typ voranstellen und ihr optional zur Deklarationszeit einen neuen Wert zuweisen. Ebenfalls erwartungsgemäß funktioniert jede gültige Methodentextsyntax – try/catch/finally-Blöcke, Variablendeklaration, Lambdaausdrücke und LINQ – problemlos.

Sogar auf der Befehlszeile bleiben andere Features von C# erhalten, wie etwa Zeichenfolgenkonstrukte (Unterscheidung von Groß- und Kleinschreibung, Zeichenfolgenliterale und Zeichenfolgeninterpolation). Wenn Sie daher Pfade verwenden oder ausgeben, müssen umgekehrte Schrägstriche mit einem C#-Escapezeichen („\“) oder einem Zeichenfolgenliteral escapet werden, wie etwa in Form doppelter umgekehrter Schrägstriche in der Ausgabe eines Pfades wie dem von „csi.exe“. Zeichenfolgeninterpolation funktioniert ebenfalls, wie aus der Beispielzeile „current directory“ in Abbildung 1 ersichtlich.

C#-Skripts lassen allerdings weit mehr als Anweisungen und Ausdrücke zu. Sie können benutzerdefinierte Typen deklarieren, Typmetadaten mithilfe von Attributen einbetten und sogar die Ausführlichkeit mithilfe von skriptspezifischen C#-Deklarativen vereinfachen. Betrachten Sie dazu das Beispiel zur Rechtschreibprüfung in Abbildung 2.

Abbildung 2 Die C#-Skriptklasse „Spell“ (Spell.csx)

#r ".\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll"
#load "Mashape.csx"  // Sets a value for the string Mashape.Key
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Spell
{
  [JsonProperty("original")]
  public string Original { get; set; }
  [JsonProperty("suggestion")]
  public string Suggestion { get; set; }
  [JsonProperty(PropertyName ="corrections")]
  private JObject InternalCorrections { get; set; }
  public IEnumerable<string> Corrections
  {
    get
    {
      if (!IsCorrect)
      {
        return InternalCorrections?[Original].Select(
          x => x.ToString()) ?? Enumerable.Empty<string>();
      }
      else return Enumerable.Empty<string>();
    }
  }
  public bool IsCorrect
  {
    get { return Original == Suggestion; }
  }
  static public bool Check(string word, out IEnumerable<string> corrections)
  {
    Task <Spell> taskCorrections = CheckAsync(word);
    corrections = taskCorrections.Result.Corrections;
    return taskCorrections.Result.IsCorrect;
  }
  static public async Task<Spell> CheckAsync(string word)
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
      $"https://montanaflynn-spellcheck.p.mashape.com/check/?text={ word }");
    request.Method = "POST";
    request.ContentType = "application/json";
    request.Headers = new WebHeaderCollection();
    // Mashape.Key is the string key available for
    // Mashape for the montaflynn API.
    request.Headers.Add("X-Mashape-Key", Mashape.Key);
    using (HttpWebResponse response =
      await request.GetResponseAsync() as HttpWebResponse)
    {
      if (response.StatusCode != HttpStatusCode.OK)
        throw new Exception(String.Format(
        "Server error (HTTP {0}: {1}).",
        response.StatusCode,
        response.StatusDescription));
      using(Stream stream = response.GetResponseStream())
      using(StreamReader streamReader = new StreamReader(stream))
      {
        string strsb = await streamReader.ReadToEndAsync();
        Spell spell = Newtonsoft.Json.JsonConvert.DeserializeObject<Spell>(strsb);
        // Assume spelling was only requested on first word.
        return spell;
      }
    }
  }
}

Größtenteils handelt es sich hier um eine standardmäßige C#-Klassendeklaration. Es gibt jedoch eine Reihe von spezifischen Features der Skripterstellung in C#. Erstens dient die Direktive „#r“ zum Verweis auf eine externe Assembly. In diesem Fall bezieht sich der Verweis auf „Newtonsoft.Json.dll“, die zur Unterstützung der Analyse von JSON-Daten benötigt wird. Beachten Sie aber, dass es sich hier um eine Direktive für den Verweis auf Dateien im Dateisystem handelt. Sie unterliegt daher nicht dem Zwang einer Escapesequenz für den umgekehrten Schrägstrich.

Zweitens können Sie das gesamte Listing als CSX-Datei speichern, die Sie dann mithilfe von „#load Spell.csx“ in das C# REPL-Fenster „importieren“ oder darin „inline bereitstellen“ können. Mithilfe der #load-Direktive können Sie zusätzliche Skriptdateien einschließen, als wären alle #load-Dateien im gleichen „Projekt“ oder der gleichen „Kompilation“ enthalten. Das Platzieren von Code in einer separaten C#-Skriptdatei ermöglicht eine Art von Dateiumgestaltung und bietet, wichtiger, die Möglichkeit, das C#-Skript permanent aufzubewahren.

Die Verwendung von Deklarationen ist ein weiteres Sprachfeature von C#, das in C#-Skripts zulässig ist und das in Abbildung 2 mehrfach genutzt wird. Beachten Sie, dass ebenso wie in C# using-Deklarationen den Gültigkeitsbereich der Datei haben. Wenn Sie daher „#load Spell.csx“ aus dem REPL-Fenster aufrufen, bleibt die using-Direktive für „Newtonsoft.Json“ außerhalb von „Spell.csx“ nicht beibehalten. Anders ausgedrückt, die using-Direktive für „Newtonsoft.Json“ aus „Spell.csx“ würde im REPL-Fenster ohne erneute explizite Deklaration im REPL-Fenster nicht permanent übernommen (und umgekehrt). Beachten Sie, dass die using-Deklarative für „static“ aus C# 6.0 ebenfalls unterstützt wird. Daher entfällt mit der Deklarative „using static System.Console“ die Notwendigkeit, Membern von „System.Console“ den Typ voranzustellen, was REPL-Befehle wie „WriteLine("Hello! Ich heiße Inigo Montoya") ermöglicht“.

Andere erwähnenswerte Konstrukte in der C#-Skripterstellung beinhalten die Verwendung von Attributen, using-Anweisungen, Eigenschafts- und Funktionsdeklarationen und Unterstützung von „async/await“. Aufgrund der letztgenannten Unterstützung ist sogar die Nutzung von „await“ im REPL-Fenster möglich:

 

(await Spell.CheckAsync("entrepreneur")).IsCorrect

Hier einige weitere Anmerkungen zur C# REPL-Oberfläche:

  • „Csi.exe“ kann nicht in der Windows PowerShell-ISE (Integrated Scripting Environment) ausgeführt werden, da das Programm direkte Konsoleneingaben erfordert, was im „simulierten“ Konsolenfenster von Windows PowerShell-ISE nicht unterstützt wird. (Aus diesem Grund ist es eine Überlegung wert, sie zur Liste nicht unterstützter Konsolenanwendungen hinzuzufügen – $psUnsupportedConsoleApplications.)
  • Es gibt keinen Befehl zum Beenden oder Schließen, um das CSI-Programm zu verlassen. Stattdessen wird STRG+C verwendet, um das Programm zu beenden.
  • Der Befehlsverlauf bleibt zwischen einzelnen csi.exe-Sitzungen, die aus der gleichen cmd.exe- oder PowerShell.exe-Sitzung gestartet werden, erhalten. Wenn Sie z. B. „csi.exe“ starten, „Console.WriteLine("HelloWorld")“ aufrufen, mit STRG+C beenden und „csi.exe“ erneut starten, wird über den Pfeil nach oben der vorhergegangene Befehl „Console.WriteLine("HelloWorld")“ angezeigt. Beim Schließen und erneuten Starten des cmd.exe-Fensters wird der Verlauf gelöscht.
  • „Csi.exe“ unterstützt den Befehl „#help REPL“, der die in Abbildung 3 gezeigte Ausgabe anzeigt.
  • „Csi.exe“ unterstützt eine Reihe von Befehlszeilenoptionen, wie in Abbildung 4 gezeigt.

Abbildung 3 Ausgabe des Befehls „REPL #help“

> #help
Keyboard shortcuts:
  Enter         If the current submission appears to be complete, evaluate it.
                Otherwise, insert a new line.
  Escape        Clear the current submission.
  UpArrow       Replace the current submission with a previous submission.
  DownArrow     Replace the current submission with a subsequent
                submission (after having previously navigated backward).
REPL commands:
  #help         Display help on available commands and key bindings.

Abbildung 4 Befehlszeilenoptionen von „Csi.exe“

Microsoft (R) Visual C# Interactive Compiler version 1.1.0.51014
Copyright (C) Microsoft Corporation. All rights reserved.
Usage: csi [option] ... [script-file.csx] [script-argument] ...
Executes script-file.csx if specified, otherwise launches an interactive REPL (Read Eval Print Loop).
Options:
  /help       Display this usage message (alternative form: /?)
  /i          Drop to REPL after executing the specified script
  /r:<file>   Reference metadata from the specified assembly file
              (alternative form: /reference)
  /r:<file list> Reference metadata from the specified assembly files
                 (alternative form: /reference)
  /lib:<path list> List of directories where to look for libraries specified
                   by #r directive (alternative forms: /libPath /libPaths)
  /u:<namespace>   Define global namespace using
                   (alternative forms: /using, /usings, /import, /imports)
  @<file>     Read response file for more options
  --          Indicates that the remaining arguments should not be
              treated as options

Wie bereits angemerkt, ermöglicht „csi.exe“ die Angabe einer standardmäßigen Profildatei zur Anpassung des Befehlsfensters:

  • Rufen Sie zum Löschen der CSI-Konsole „Console.Clear“ auf. (Erwägen Sie eine Deklarative „using static System.Console“, um Unterstützung für den einfachen Aufruf von „Clear“ hinzuzufügen.)
  • Wenn Sie einen mehrere Zeilen umfassenden Befehl eingeben und in einer früheren Zeile einen Fehler gemacht haben, können Sie mithilfe von STRG+Z gefolgt von der EINGABETASTE abbrechen und zu einer leeren Eingabeaufforderung zurückkehren, ohne den Befehl auszuführen (beachten Sie, dass das ^Z in der Konsole angezeigt wird).

Das interaktive Fenster von Visual Studio C#

Wie ich bereits erwähnt hatte, gibt es außerdem ein neues interaktives Visual Studio C#-Fenster in Update 1, wie in Abbildung 5 gezeigt. Das interaktive C#-Fenster wird aus dem Menü „Ansicht“ | „Weitere Fenster“ | „C# interaktiv“ gestartet, das ein zusätzliches angedocktes Fenster öffnet. Wie das Fenster von „csi.exe“ handelt es sich um ein C# REPL-Fenster, jedoch mit einigen zusätzlichen Features. Erstens sind farbige Syntaxhervorhebung und IntelliSense vorhanden. Zweitens erfolgt die Kompilierung in Echtzeit während der Eingabe, daher werden Syntaxfehler u.ä. mit einer roten Wellenlinie unterstrichen.

Deklarieren einer C#-Skriptfunktion außerhalb einer Klasse mithilfe des interaktiven C#-Fensters von Visual Studio
Abbildung 5 Deklarieren einer C#-Skriptfunktion außerhalb einer Klasse mithilfe des interaktiven C#-Fensters von Visual Studio

Eine Assoziation mit dem interaktiven C#-Fenster, die sich naturgemäß häufig aufdrängt, sind die Direkt- und Befehlsfenster von Visual Studio. Zwar gibt es Überschneidungen – schließlich sind beide REPL-Fenster, in denen .NET-Anweisungen ausgeführt werden können – jedoch dienen sie ganz unterschiedlichen Zwecken. Das C#-Direktfenster ist direkt an den Debugkontext Ihrer Anwendung gebunden und ermöglicht daher das Einfügen zusätzlicher Anweisungen in den Kontext, das Untersuchen von Daten innerhalb einer Debugsitzung und sogar das Bearbeiten und Aktualisieren von Daten und Debugkontext. In ähnlicher Weise stellt das Befehlsfenster ein CLI zum Bearbeiten von Visual Studio bereit, einschließlich der Ausführung der verschiedenen Menüs, jedoch im Befehlsfenster statt in den Menüs selbst. (Durch Ausführen des Befehls „View.C#Interactive“ wird beispielsweise das interaktive C#-Fenster geöffnet.) Im Gegensatz dazu können Sie im interaktiven C#-Fenster C# ausführen, einschließlich aller Features, die im vorhergehenden Abschnitt für die C# REPL-Oberfläche erörtert wurden. Das interaktive C#-Fenster hat jedoch keinen Zugriff auf den Debugkontext. Es stellt eine völlig unabhängige C#-Sitzung dar, ohne Handles für den Debugkontext, ja nicht einmal für Visual Studio. Wie „csi.exe“ stellt es eine Umgebung dar, in der Sie schnell mit C#- und .NET-Codeausschnitten experimentieren können, ohne jeweils eine weitere Visual Studio-Konsole oder ein neues Komponententestprojekt starten zu müssen. Statt ein eigenes Programm starten zu müssen, ist das interaktive C#-Fenster jedoch in Visual Studio gehostet, wo sich der Entwickler vermutlich sowieso aufhält.

Hier sind einige Anmerkungen zum interaktiven C#-Fenster:

  • Das interaktive C#-Fenster unterstützt eine Reihe von zusätzlichen REPL-Befehlen, die sich nicht in „csi.exe“ finden, darunter:
    • „#cls/#clear“, um den Inhalt des Editor-Fensters zu löschen
    • „#reset“, um den Ausgangsstatus der Ausführungsumgebung wiederherzustellen, den Befehlsverlauf dabei aber beizubehalten
  • Die Tastenkombinationen sind etwas ungewöhnlich, wie die Ausgabe von „#help“ in Abbildung 6 zeigt.

Abbildung 6 Tastenkombinationen für das interaktive C#-Fenster

Eingeben Wenn die aktuelle Eingabe vollständig zu sein scheint, werten Sie sie aus. Fügen Sie andernfalls eine weitere Zeile ein.
STRG+EINGABE Auswerten der aktuellen Eingabe innerhalb der aktuellen Eingabe.
UMSCHALT+EINGABE Neue Zeile einfügen.
ESCAPE Aktuelle Eingabe löschen.
ALT+PFEIL-NACH-OBEN Aktuelle Eingabe durch eine vorhergehende Eingabe ersetzen.
ALT-PFEIL-NACH-UNTEN Aktuelle Eingabe durch eine nachfolgende Eingabe ersetzen (nachdem zuvor zurück navigiert wurde).
STRG+ALT+PFEIL-NACH-OBEN Aktuelle Eingabe durch eine vorhergehende Eingabe ersetzen, die mit dem gleichen Text beginnt.
STRG+ALT+PFEIL-NACH-UNTEN Aktuelle Eingabe durch eine nachfolgende Eingabe ersetzen, die mit dem gleichen Text beginnt (nachdem zuvor zurück navigiert wurde).
PFEIL-NACH-OBEN

Am Ende der aktuellen Eingabe die aktuelle Eingabe durch eine vorhergehende Eingabe ersetzen.

An anderen Positionen wird der Cursor um eine Zeile nach oben verschoben.

PFEIL-NACH-UNTEN

Am Ende der aktuellen Eingabe die aktuelle Eingabe durch eine nachfolgende Eingabe ersetzen (nachdem zuvor zurück navigiert wurde).

An anderen Positionen wird der Cursor um eine Zeile nach unten verschoben.

STRG+K, STRG+EINGABE Die Auswahl am Ende des interaktiven Puffers einfügen, Einfügemarke am Ende der Eingabe belassen.
STRG+E, STRG+EINGABE Die Auswahl vor allen ausstehenden Inhalten im interaktiven Puffer einfügen und ausführen.
Strg+A Beim ersten Drücken wird die Eingabe ausgewählt, die den Cursor enthält. Beim zweiten Drücken wird der gesamte Text im Fenster ausgewählt.

Es muss daran erinnert werden, dass ALT+PFEIL-NACH-OBEN/PFEIL-NACH-UNTEN die Tastenkombinationen für das Abrufen des Befehlsverlaufs sind. Microsoft hat sich für diese anstelle der einfacheren PFEIL-NACH-OBEN/PFEIL-NACH-UNTEN entschieden, da die Oberfläche des interaktiven Fensters der eines normalen Codefensters von Visual Studio entsprechen sollte.

  • Da das interaktive C#-Fenster in Visual Studio gehostet wird, bieten sich hier nicht die gleichen Anlässe zum Übergeben von Verweisen, Verwenden von Deklarativen oder Importieren über die Befehlszeile wie in „csi.exe“. Stattdessen lädt das interaktive C# Fenster seinen standardmäßigen Ausführungskontext aus „C:\Programme (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies\CSharpInteractive.rsp“, wo die referenzierten Assemblys standardmäßig genannt sind:
# This file contains command-line options that the C# REPL
# will process as part of every compilation, unless
# \"/noconfig\" option is specified in the reset command.
/r:System
/r:System.Core
/r:Microsoft.CSharp
/r:System.Data
/r:System.Data.DataSetExtensions
/r:System.Xml
/r:System.Xml.Linq
SeedUsings.csx

Außerdem verweist die Datei „CSharpInteractive.rsp“ auf eine standardmäßige Datei „C:\Programme (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies\SeedUsings.csx“:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

Die Kombination dieser beiden Dateien stellt den Grund dar, warum „Console.WriteLine“ und „Environment.CurrentDirectory“ anstelle der vollqualifizierten Angaben „System.Console.WriteLine“ und „System.Environ-ment.Current­Directory“ verwendet werden können. Außerdem eröffnet der Verweis auf Assemblys wie „Microsoft.CSharp“ die Möglichkeit zur Verwendung von Sprachfeatures wie „dynamic“ ohne weitere Maßnahmen. (Das Ändern dieser Dateien ist das, was beim Ändern Ihres „Profils“ oder Ihrer „Einstellungen“ geschieht, sodass die Änderungen zwischen Sitzungen gespeichert werden.)

Mehr zur C#-Skriptsyntax

Ein Punkt, den Sie bei der C#-Skriptsyntax beachten sollten, besteht darin, dass viel von dem zeremoniellen Drumherum, das für standardmäßiges C# wichtig ist, in C#-Skripts weitgehend optional wird. Beispielsweise müssen Textkörper von Methoden nicht innerhalb einer Funktion auftreten, und C#-Skriptfunktionen können außerhalb der Einschränkungen einer Klasse deklariert werden. Sie können beispielsweise eine NuGet-Installationsfunktion definieren, die direkt im REPL-Fenster angezeigt wird, wie in Abbildung 5 dargestellt. Außerdem unterstützt C# – das mag überraschen – das Deklarieren von Namespaces nicht. Sie können beispielsweise die Klasse „Spell“ nicht mit einem Namespace „Grammar“ umschließen: namespace Grammar { class Spell {} }.

Es ist wichtig, zu verstehen, dass Sie das gleiche Konstrukt (Variable, Klasse, Funktion usw.) wieder und wieder deklarieren können. Die letzte Deklaration führt Shadowing für alle früheren Deklarationen aus.

Ein weiterer wichtiger Punkt, der bekannt sein muss, ist das Verhalten des Befehle abschließenden Semikolons. Anweisungen (z. B. Variablenzuweisungen) erfordern ein Semikolon. Ohne das Semikolon fordert das REPL-Fenster zur Eingabe weiterer Eingaben auf (mithilfe eines Punkts), bis das Semikolon eingegeben wird. Ausdrücke werden andererseits ohne Semikolon ausgeführt. Daher startet „System.Diagnostics.Pro­cess.Start("notepad")“ den Editor auch ohne das abschließende Semikolon. Da außerdem der Aufruf der Methode „Start“ einen Prozess zurückgibt, wird die Zeichenfolgenausgabe des Ausdrucks an der Befehlszeile angezeigt: [System.Diagnostics.Process (Notepad)]. Beim Abschließen eines Ausdrucks mit einem Semikolon wird die Ausgabe jedoch ausgeblendet. Das Aufrufen von „Start“ mit einem Semikolon am Ende erzeugt daher keinerlei Ausgabe, obwohl der Editor natürlich trotzdem gestartet wird. Und selbstverständlich gibt „Console.WriteLine("It would take a miracle.");“ den Text trotzdem aus, auch mit Semikolon, da die Methode selbst die Ausgabe anzeigt (nicht den Rückgabewert der Methode).

Die Unterscheidung zwischen Ausdrücken und Anweisungen kann gelegentlich zu subtilen Unterschieden führen. Beispielsweise bewirkt die Anweisungszeichenfolge „text= "There’s a shortage of perfect b….";“ keine Ausgabe, „text="Stop that rhyming and I mean it"“ gibt jedoch die zugewiesene Zeichenfolge zurück (da die Anweisung den zugewiesenen Wert zurückgibt und kein Semikolon die Ausgabe verhindert).

Die C#-Skriptdirektiven für den Verweis auf zusätzliche Assemblys (#r) und das Importieren von vorhandenen C#-Skripts (#load) sind wunderbare Zugaben. (Man kann sich komplexe Lösungen, wie etwa project.json-Dateien, zum Verfolgen des gleichen Zwecks vorstellen, die bei weitem nicht so elegant wären.) Unglücklicherweise werden zum Zeitpunkt, da dieser Text entsteht, keine NuGet-Pakete unterstützt. Der Verweis auf eine Datei aus NuGet setzt die Installation des Pakets in einem Verzeichnis voraus; anschließend muss auf die bestimmte DLL mithilfe der Direktive „#r“ verwiesen werden. (Microsoft hat aber versichert, dass NuGet-Unterstützung noch kommt.)

Beachten Sie, dass zurzeit die Direktiven auf bestimmte Dateien verweisen. Sie können z. B. keine Variable in der Direktive angeben. Das liegt zwar für eine Direktive im erwartbaren Rahmen, es verhindert aber die Möglichkeit zum dynamischen Laden von Assemblys. Beispielsweise können Sie dynamisch „nuget.exe install“ aufrufen, um eine Assembly zu extrahieren (auch dazu wieder Abbildung 5). Das ermöglicht Ihrer CSX-Datei aber noch keine dynamische Bindung an das extrahierte NuGet-Paket, da es keine Möglichkeit gibt, den Assemblypfad dynamisch an die #r-Direktive zu übergeben.

Ein C#-CLI

Ich muss gestehen, dass mich eine Hassliebe mit Windows PowerShell verbindet. Ich liebe die Bequemlichkeit, mit Microsoft .NET Framework auf der Befehlszeile arbeiten zu können, und die Möglichkeit, .NET-Objekte über die Pipe zu übergeben, vor allem im Vergleich mit so vielen früheren CLIs. Nachdem das gesagt ist: Wenn es um die Sprache C# geht, bin ich Partisan – ich liebe ihre Eleganz und Stärke. (Ich bin heute noch von den Spracherweiterungen beeindruckt, die LINQ ermöglicht haben.) Daher hat die Vorstellung, dass sich die Breite von .NET in Windows PowerShell mit der Eleganz der Sprache C# verbinden ließe, für mich bedeutet, dass ich mich der C# REPL als Ersatz für Windows PowerShell genähert habe. Nach dem Starten von „csi.exe“ habe ich sofort Befehle wie „cd“, „dir“, „ls“, „pwd“, „cls“, „alias“ usw. ausprobiert. Naja, ich war natürlich enttäuscht, weil keiner von ihnen funktionierte. Nachdem sich die Erfahrung gesetzt und ich mich mit dem C#-Team ausgetauscht hatte, wurde mir klar, dass ein Ersatz für Windows PowerShell nicht der Schwerpunkt der Entwicklung für Version 1 war. Außerdem geht es um .NET Framework, und daher werden Erweiterungen sowohl durch Hinzufügen eigener Funktionen für die vorgenannten Befehle als auch sogar durch Aktualisieren der C#-Skriptimplementierung in Roslyn unterstützt. Ich machte mich sofort daran, Funktionen für solche Befehle zu definieren. Der Beginn einer solchen Bibliothek steht auf GitHub unter github.com/CSScriptEx zum Download bereit.

Für die unter Ihnen, die auf der Suche nach einem funktionsreicheren C# CLI sind, das die Liste der vorgenannten Befehle ohne weitere Konfiguration unterstützt, werfen Sie einen Blick auf ScriptCS unter „scriptcs.net“ (auch auf GitHub unter github.com/scriptcs). Es nutzt ebenfalls Roslyn und schließt „alias“, „cd“, „clear“, „cwd“, „exit“, „help“, „install“, „references“, „reset“, „scriptpacks“, „usings“ und „vars“ ein. Beachten Sie, dass in ScriptCS das Befehlspräfix ein Doppelpunkt ist (wie in „:reset“) anstelle eines Gatterzeichens (wie in „#reset“). Als zusätzlichen Bonus fügt ScriptCS Visual Studio-Code außerdem Unterstützung für CSX-Dateien mit farbiger Hervorhebung und IntelliSense hinzu.

Zusammenfassung

Zumindest zum gegenwärtigen Zeitpunkt ist der Zweck der C# REPL-Oberfläche nicht, Windows PowerShell oder sogar „cmd.exe“ zu ersetzen. Wenn Sie sich ihr so nähern, werden Sie enttäuscht. Stattdessen schlage ich vor, dass Sie C#-Skripterstellung und die REPL CLIs als schlanken Ersatz für Visual Studio | „Neues Projekt“: „UnitTestProject105“ oder das ähnlich gelagerte „dotnetfiddle.net“ betrachten. Dies sind auf C# und .NET abzielende Verfahren, um Ihr Verständnis der Sprache und von .NET APIs zu fördern. Die C# REPL bietet eine Möglichkeit, den Code für kurze Codeausschnitte oder Programmkomponenten zu erstellen, an denen Sie herumdoktern können, bis sie für das Ausschneiden und Einfügen in größere Programme bereit sind. Sie ermöglicht Ihnen, umfangreichere Skripts zu erstellen, deren Syntax beim Schreiben des Codes validiert wird (selbst auf Kleinigkeiten wie Nichtübereinstimmung der Groß-/Kleinschreibung), statt Sie zu zwingen, das Skript auszuführen, um dann festzustellen, dass Sie einen Tippfehler gemacht haben. Sobald Sie ihre Rolle verstehen, werden Skripterstellung in C# und die interaktiven Fenster ein Vergnügen, das Tool, nach dem Sie schon seit Version 1.0 gesucht haben.

So interessant wie C# REPL und C#-Skripterstellung für sich genommen schon sind, bedenken Sie, dass sie auch ein Sprungbrett für ein Erweiterungsframework für Ihre eigene Anwendung darstellen – im Stil von Visual Basic für Applikationen (VBA). Mit einem interaktiven Fenster und der Unterstützung für C#-Skripterstellung können Sie sich eine Welt vorstellen – in nicht zu ferner Zukunft– in der Sie Ihren eigenen Anwendungen wieder .NET-„Makro“ hinzufügen können – ohne erst eine eigene Sprache, einen Parser und einen Editor zu erfinden. Das wäre für mich wirklich ein altes COM-Feature, das es wert wäre, in die moderne Welt herübergebracht zu werden.


Mark Michaelis ist der Gründer von IntelliTect und arbeitet als leitender technischer Architekt und Trainer. Seit fast zwei Jahrzehnten ist er ein Microsoft MVP und Microsoft-Regionalleiter seit 2007. Michaelis arbeitet in verschiedenen Microsoft-Softwareentwicklungs-Reviewteams mit, einschließlich C#, Microsoft Azure, SharePoint und Visual Studio ALM. Er hält häufig Vorträge bei Entwicklerkonferenzen und hat viele Bücher geschrieben, einschließlich seines letzten „Essential C# 6.0 (5th Edition)“ (itl.tc/­EssentialCSharp). Sie können ihn auf Facebook unter facebook.com/Mark.Michaelis, über seinen Blog unter IntelliTect.com/Mark, auf Twitter: @markmichaelis oder per E-Mail unter mark@IntelliTect.com erreichen.

Unser Dank gilt dem folgenden technischen Experten bei Microsoft für die Durchsicht dieses Artikels: Kevin Bost und Kasey Uhlenhuth