C# Interactif

Brouillon

À venir

C# Interactif

C# interactif est un REPL (Read-Eval-Print-Loop), dans la même veine que les langages shell. Il a été introduit fin 2015, est accessible directement depuis Visual Studio et permet de rentrer des déclarations C#. Le résultat est immédiatement visible dans la console lorsqu'on valide la déclaration avec la touche entrée.

CSI

En somme, plus besoin de ConsoleApplication, de solutions et projets complets pour exécuter une portion de code. C'est l'outil idéal pour tester incrémentalement vos modifications ou votre API. Découvrons ensemble comment l'utiliser !

Exemple

Pour illustrer son utilisation, nous allons utiliser l'API NuGet pour récupérer les N packages les plus populaires du moment. Voici une implémentation de cette fonctionnalité, à l'aide du package NuGet.Core prévu à cet effet.

using System.Collections.Generic;
using System.Linq;
using NuGet;

namespace CSharpInteractive 
{
    public class MostPopularPackagesFinder
    {
        IPackageRepository Repository { get; }

        public MostPopularPackagesFinder(string repositoryUrl)
        {
            Repository = PackageRepositoryFactory.Default.CreateRepository(repositoryUrl);
        }

        public IEnumerable<string> Find(int count)
        {
            return Repository
                .GetPackages()
                .OrderByDescending(package => package.DownloadCount)
                .FindLatestVersion()
                .Take(count)
                .AsEnumerable()
                .Select(package => package.GetFullName());
        }
    }
}

Il ne reste plus qu'à l'exécuter dans C# interactif, et le tour est joué. Pas besoin de ConsoleApplication pour essayer notre code !

C# interactif dans Visual Studio 2017

Visual Studio 2015 et 2017 permettent d'exécuter du code dans C# interactif via le menu contextuel sur votre projet.

Intégration dans Visual Studio 2017

Après compilation, on peut exécuter le script suivant dans la console interactive :

#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Core.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Xml.Linq.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Data.DataSetExtensions.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\Microsoft.CSharp.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Data.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Net.Http.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Xml.dll"
#r "c:\users\ali\source\repos\CSharpInteractive\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll"
#r "c:\users\ali\source\repos\CSharpInteractive\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll"
#r "CSharpInteractive.dll"

using CSharpInteractive;
WriteLine("Finding most popular packages..."); // Pas besoin de préfixer par System.Console, il est en using static pour tous les scripts.
var finder = new MostPopularPackagesFinder("https://packages.nuget.org/api/v2");
finder.Find(5) // Pas de terminaison ";" ! Le REPL s'occupe de l'affichage.

Son exécution produit bien le résultat attendu :

Finding most popular packages...
Enumerable.WhereSelectEnumerableIterator<IPackage, string> { "Newtonsoft.Json 11.0.2", "jQuery 3.3.1", "EntityFramework 6.2.0", "Microsoft.AspNet.Razor 3.2.6", "Microsoft.AspNet.Mvc 5.2.6" }

Nous allons maintenant voir en détail les possibilités offertes par le scripting C#, ainsi que ses limitations.

Anatomie d'un script C#

Un script C# ne suit pas exactement les mêmes règles que le C# classique, même s'ils restent très proches. Des fonctionnalités supplémentaires sont disponibles pour pouvoir écrire un script indépendant de tout projet ou solution :

  • Directive #load "script.csx" : non illustrée dans l'exemple précédent, elle permet de charger un autre script et donc de factoriser les parties de code que vous souhaitez mettre en commun pour d'autres scripts.
  • Directive #r : elle permet de réferencer une assembly et donc d'utiliser les types qui s'y trouvent. C'est l'équivalent de la référence pour un .csproj. Au lancement du C# interactif via Visual Studio, elles sont automatiquement ajoutées.
  • Fonctionnalités REPL : toute méthode ayant un type de retour sera imprimé dans la console, via Console.WriteLine(result) ou en omettant le ; de terminaison d'une déclaration C#.

Limitations

Bien qu'un script C# vous permettent de vous passer de projets pour des tests rapides, certaines fonctionnalités des projets classiques peuvent ne pas être supportées. Voici une liste non exhaustive des problèmes auxquels vous pourrez vous heurter et vous ramener à un projet de tests unitaires classique.

.NETStandard

Visual Studio 2017 n'offre pas de C# interactif pour les librairies ciblant .NET Standard. La CLI de .NET Core n'inclue pas non plus la fonctionnalité (de même pour F# et VB interactif).

NuGet

Il n'est pas possible de référencer un paquet NuGet directement (il faut référencer la/les dll correspondante(s) avec une directive #r). Il est possible de faire un script de setup qui exécuterait nuget restore pour pallier ce problème, mais il faut penser à l'exécuter avant d'appeler csi (l'exécutable C# interactif).

Il existe une issue sur le GitHub de Roslyn à ce sujet.

API System.Reflection

Le résultat d'un appel à l'API System.Reflection peut être différent dans C# interactif par rapport à un projet classique. Par exemple, un champ ou une propriété internal sera considérée public par C# interactif, ce qui sera un problème pour un sérialiseur qui introspecte les types à sérialiser.

ScriptCs

ScriptCs facilite l'utilisation du REPL et des scripts en apportant les packages NuGet pour vos scripts via scriptcs -install ou en réalisant un bootstrap customisé pour tous vos scripts. Si vous cherchez à construire un environnement de scripting utilisant du C#, ScriptCs peut valoir le détour.

Adrian Lissot

Ingénieur en Systèmes Logiciels et Réseaux diplômé de TELECOM Bretagne

Profil de l'auteur