Maggio 2017

Volume 32 Numero 5

Il presente articolo è stato tradotto automaticamente.

.NET Core - Generazione di codice multipiattaforma con Roslyn e .NET Core

Da Alessandro Del Del

.NET core è modulare, aprire i set di strumenti di origine e multipiattaforma che consente di compilare applicazioni .NET di nuova generazione per Windows, Linux e macOS (microsoft.com/net/core/platform). Può inoltre essere installato su Windows 10 per IoT distribuzione e viene eseguito su dispositivi, ad esempio Raspberry PI. .NET core è una piattaforma potente che include il runtime, librerie e compilatori, con supporto completo per lingue quali c#, F # e Visual Basic. Ciò significa che è possibile scrivere codice in c# non solo in Windows, ma anche in sistemi operativi diversi perché la piattaforma per compilatori .NET (github.com/dotnet/roslyn), noto anche come "Progetto Roslyn," fornisce Apri origine, le API di analisi del codice multipiattaforma compilatori ricca. Come un'importante implicazione, è possibile sfruttare le APIs Roslyn per eseguire molte operazioni correlate al codice, in sistemi operativi diversi, ad esempio l'analisi del codice, la generazione di codice e la compilazione. In questo articolo vengono illustrati i passaggi necessari per configurare un progetto c# in .NET Core per l'utilizzo delle APIs Roslyn e vengono illustrati alcuni scenari di compilazione e la generazione di codice interessa. Vengono inoltre presentate alcune tecniche di base per la Reflection per richiamare ed eseguire il codice compilato con Roslyn su .NET Core. Se non si ha familiarità con Roslyn, è opportuno leggere prima gli articoli seguenti:

L'installazione di base di .NET SDK

Il primo passaggio è l'installazione di .NET Core e SDK. Se si lavora su Windows ed è stato installato Visual Studio 2017, .NET Core è già incluso se il carico di lavoro di sviluppo multipiattaforma di .NET Core è stata selezionata al momento dell'installazione di Visual Studio Installer. In caso contrario, aprire Visual Studio Installer, selezionare il carico di lavoro e fare clic su Modifica. Se si sta lavorando in Windows ma senza affidarsi a Visual Studio 2017, o si sta lavorando su Linux o macOS, è possibile installare manualmente .NET Core e utilizzare codice di Visual Studio come ambiente di sviluppo (code.visualstudio.com). Quest'ultimo è lo scenario di che questo articolo verrà illustrato come Visual Studio Code è multipiattaforma. Pertanto, è di grande supporto per .NET Core. Inoltre, è necessario installare l'estensione del linguaggio c# per Visual Studio Code (bit.ly/29b1Ppl). La procedura per installare .NET Core è diversa a seconda del sistema operativo, quindi seguire le istruzioni in bit.ly/2mJArWx. Assicurarsi di installare la versione più recente. Vale la pena sottolineare che le ultime versioni di .NET Core non supportano il formato di file di Project, ma supportano invece il formato di file con estensione csproj più comune con MSBuild.

Lo scaffolding di un'applicazione di base di .NET in c#

Con .NET Core, è possibile creare applicazioni Console e applicazioni Web. Per le applicazioni Web, Microsoft sta ulteriori modelli disponibili, oltre il modello ASP.NET di base, come .NET Core passa la roadmap. Poiché Visual Studio Code è un editor leggero, non fornisce modelli di progetto di Visual Studio. Ciò significa che è necessario creare un'applicazione dalla riga di comando all'interno di una cartella il cui nome sarà anche il nome dell'applicazione. Nell'esempio seguente si basa su istruzioni per Windows, ma gli stessi concetti si applicano a macOS e Linux. Per iniziare, aprire un prompt dei comandi e passare a una cartella sul disco. Ad esempio, si supponga di che avere una cartella denominata C:\Apps, accedere a questa cartella e creare una nuova sottocartella denominata RoslynCore, utilizzando i comandi seguenti:

> cd C:\Apps
> md RoslynCore
> cd RoslynCore

Di conseguenza, RoslynCore sarà il nome dell'applicazione di esempio illustrato in questo articolo. Sarà un'applicazione Console, che rappresenta la soluzione ideale per scopi didattici e semplifica l'approccio alla codifica con Roslyn. È inoltre possibile utilizzare le stesse tecniche nelle applicazioni Web ASP.NET Core. Per creare un nuovo progetto vuoto per un'applicazione Console, è sufficiente digitare la seguente riga di comando:

> dotnet new console

In questo modo, .NET Core scaffolds un progetto c# per un'applicazione Console denominata RoslynCore. È ora possibile aprire la cartella del progetto con Visual Studio Code. Il modo più semplice è digitando il seguente comando:

> code .

Naturalmente, è possibile aprire Visual Studio Code dal menu Start di Windows e quindi aprire manualmente una cartella di progetto. Dopo aver inserito un file di codice c#, richiede l'autorizzazione per generare alcune risorse necessarie e per ripristinare alcuni pacchetti NuGet (vedere figura 1).

Codice di Visual Studio è necessario aggiornare il progetto
Figura 1 è necessario aggiornare il progetto Visual Studio Code

Il passaggio successivo è l'aggiunta dei pacchetti NuGet necessari per l'utilizzo di Roslyn.

Aggiungere i pacchetti Roslyn NuGet

Come sapete, APIs Roslyn può essere utilizzata per l'installazione di alcuni pacchetti NuGet dalla gerarchia di Microsoft.CodeAnalysis. Prima di installare questi pacchetti, è importante chiarire come APIs Roslyn si inserisce nel sistema di base .NET. Se è mai lavorato con Roslyn su .NET Framework, potrebbe essere utilizzato per sfruttare il set completo di Roslyn APIs. .NET core si basa su librerie .NET Standard, il che significa che solo le librerie di Roslyn che supportano .NET Standard possono essere utilizzate in .NET Core. Al momento della stesura di questo articolo, la maggior parte delle APIs Roslyn sono già disponibile in .NET Core, compreso (ma non è limitato a) l'API del compilatore (con l'emissione e l'API di diagnostica) e le API di aree di lavoro. Solo alcune API non sono ancora portabili, ma Microsoft sta investendo capitali estremamente Roslyn e .NET Core, pertanto è ragionevole aspettarsi la piena compatibilità .NET Standard nelle versioni future. Un esempio reale di un'applicazione multipiattaforma che viene eseguito su .NET Core è OmniSharp (bit.ly/2mpcZeF), che sfrutta le APIs Roslyn per consentire la maggior parte delle funzionalità di editor del codice, ad esempio elenchi di completamento e l'evidenziazione della sintassi.

In questo articolo verrà illustrato come utilizzare il compilatore e le API di diagnostica. A tale scopo, è necessario aggiungere il pacchetto Microsoft.CodeAnalysis.CSharp NuGet al progetto. Con il nuovo sistema di progetti .NET Core basato su MSBuild, l'elenco dei pacchetti NuGet è ora inclusa nel file di progetto csproj. In Visual Studio 2017, è possibile utilizzare il client dell'interfaccia utente per NuGet per scaricare, installare e gestire i pacchetti, ma in Visual Studio Code non esiste alcuna opzione equivalente. Fortunatamente, è sufficiente aprire il file con estensione csproj e individuare il nodo < ItemGroup > che contiene gli elementi < PackageReference >, ognuno dei quali rappresenta un pacchetto NuGet necessario. Modificare il nodo come segue:

<ItemGroup>
  ...
  <PackageReference Include="Microsoft.CodeAnalysis.CSharp"
    Version="2.0.0 " />
  <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>

Si noti che aggiungendo un riferimento al pacchetto Microsoft.CodeAnalysis.CSharp consente di accedere alle API del compilatore c#, e che il pacchetto System.Runtime.Loader è necessario per la Reflection e verrà utilizzato più avanti nell'articolo.

Quando si salvano le modifiche, Visual Studio Code rileverà i pacchetti NuGet mancanti e sarà disponibile per il ripristino.

Analisi del codice: Analisi di testo di origine e la generazione di nodi di sintassi

Nel primo esempio riguarda l'analisi del codice e viene illustrato come analizzare il testo del codice sorgente e generare nuovi nodi di sintassi. Ad esempio, si supponga di avere il seguente oggetto business semplice e si desidera generare una classe di modello di visualizzazione basata su di essa:

namespace Models
{
  public class Item
  {
    public string ItemName { get; set }
  }
}

Il testo per l'oggetto business potrebbe provenire da origini diverse, ad esempio un file di codice c# o una stringa nel codice o anche l'input dell'utente. Con l'API di analisi del codice, è possibile analizzare il testo di origine e generare un nuovo nodo di sintassi che il compilatore può comprendere e gestire. Ad esempio, si consideri il codice illustrato nella figura 2, che analizza una stringa contenente una definizione di classe, ottiene il nodo corrispondente sintassi e chiama un nuovo metodo statico che genera un modello di visualizzazione dal nodo di sintassi.

Figura 2 analisi del codice sorgente e il recupero di un nodo di sintassi

using System;
using RoslynCore;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
class Program
{
  static void Main(string[] args)
  {
    GenerateSampleViewModel();
  }
  static void GenerateSampleViewModel()
  {
    const string models = @"namespace Models
{
  public class Item
  {
    public string ItemName { get; set }
  }
}
";
    var node = CSharpSyntaxTree.ParseText(models).GetRoot();
    var viewModel = ViewModelGeneration.GenerateViewModel(node);
    if(viewModel!=null)
      Console.WriteLine(viewModel.ToFullString());
    Console.ReadLine();
  }
}

Il GenerateViewModel metodo sta per essere definita in una classe statica denominata ViewModelGeneration, aggiungere un nuovo file denominato ViewModelGeneration.cs al progetto. Il metodo cerca una definizione di classe nel nodo di sintassi di input (a scopo dimostrativo, la prima istanza di un oggetto ClassDeclarationSyntax), quindi crea un nuovo modello di visualizzazione in base a nome e i membri della classe. Figura 3 viene illustrato quanto descritto.

Figura 3, la generazione di un nuovo nodo di sintassi

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
namespace RoslynCore
{
  public static class ViewModelGeneration
  {
    public static SyntaxNode GenerateViewModel(SyntaxNode node)
    {
      // Find the first class in the syntax node
      var classNode = node.DescendantNodes()
       .OfType<ClassDeclarationSyntax>().FirstOrDefault();
      if(classNode!=null)
      {
        // Get the name of the model class
        string modelClassName = classNode.Identifier.Text;
        // The name of the ViewModel class
        string viewModelClassName = $"{modelClassName}ViewModel";
        // Only for demo purposes, pluralizing an object is done by
        // simply adding the "s" letter. Consider proper algorithms
        string newImplementation =
          $@"public class {viewModelClassName} : INotifyPropertyChanged
{{
public event PropertyChangedEventHandler PropertyChanged;
// Raise a property change notification
protected virtual void OnPropertyChanged(string propname)
{{
  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}}
private ObservableCollection<{modelClassName}> _{modelClassName}s;
public ObservableCollection<{modelClassName}> {modelClassName}s
{{
  get {{ return _{modelClassName}s; }}
  set
  {{
    _{modelClassName}s = value;
    OnPropertyChanged(nameof({modelClassName}s));
  }}
}}
public {viewModelClassName}() {{
// Implement your logic to load a collection of items
}}
}}
";
          var newClassNode =
            CSharpSyntaxTree.ParseText(newImplementation).GetRoot()
            .DescendantNodes().OfType<ClassDeclarationSyntax>()
            .FirstOrDefault();
          // Retrieve the parent namespace declaration
          if(!(classNode.Parent is NamespaceDeclarationSyntax)) return null;
          var parentNamespace = (NamespaceDeclarationSyntax)classNode.Parent;
          // Add the new class to the namespace and adjust the white spaces
          var newParentNamespace =
            parentNamespace.AddMembers(newClassNode).NormalizeWhitespace();
          return newParentNamespace;
        }
      }
      else
      {
        return null;
      }
    }
  }
}

Nella prima parte del codice in figura 3, è possibile vedere come il modello di visualizzazione viene innanzitutto rappresentato come una stringa, con interpolazione di stringhe che semplifica le operazioni per specificare i nomi di oggetti e membri in base al nome di classe originale. In questo scenario di esempio plurali vengono generati solo mediante l'aggiunta di una "s" per il nome di oggetto o membro. nel codice del mondo reale è necessario utilizzare algoritmi di pluralizzazione più specifici.

Nella seconda parte di figura 3, il codice richiama CSharpSyntaxTree.ParseText per l'analisi del testo di origine in un SyntaxTree. GetRoot viene richiamato per recuperare il SyntaxNode per la nuova struttura; con DescendantNodes(). OfType < ClassDeclarationSyntax > (), il codice recupera solo i nodi di sintassi che rappresentano una classe, selezionare solo il primo con FirstOrDefault. Recupero di prima classe nel nodo sintassi è sufficiente per ottenere lo spazio dei nomi padre in cui verrà inserita la nuova classe di modello di visualizzazione. Ottenere uno spazio dei nomi è possibile tramite la proprietà padre di un ClassDeclarationSyntax esegue il cast in un oggetto NamespaceDeclarationSyntax. Poiché una classe può essere annidata in un'altra classe, il codice controlla questa possibilità verificando che padre è di tipo NamespaceDeclarationSyntax. Nella parte finale del codice aggiunge il nuovo nodo di sintassi per la classe di modello di visualizzazione per lo spazio dei nomi padre, restituiscono questo come un nodo di sintassi. Se ora si preme F5, verrà visualizzato il risultato della generazione del codice nella Console di Debug, come illustrato nella figura 4.

Classe di modello di visualizzazione è stata generata correttamente
Figura 4 che è stata generata correttamente la classe di modello di visualizzazione

La classe di modello di visualizzazione generata è un SyntaxNode che è possibile utilizzare il compilatore c#, pertanto può essere ulteriormente modificato, analizzato in termini di informazioni diagnostiche, compilato in un assembly con le API di emettere e utilizzato tramite Reflection.

Recupero di informazioni di diagnostica

Se il testo di origine provenienza da una stringa, un file o un utente di input, è possibile sfruttare le API di diagnostica per recuperare informazioni di diagnostica sui problemi di codice, ad esempio errori e avvisi. Tenere presente che le API di diagnostica non solo consentono il recupero degli errori e avvisi, inoltre, consente la scrittura di analizzatori e refactoring del codice. Continuando con l'esempio precedente, è consigliabile controllare gli errori sintattici nel testo di origine originale prima di tentare di generare una classe di modello di visualizzazione. A tale scopo, è possibile richiamare il metodo SyntaxNode.GetDiagnostics, che restituisce un oggetto IEnumerable < Microsoft.CodeAnalysis.Diagnostic >, se presente. Dare un'occhiata figura 5, che offre una versione estesa della classe ViewModelGeneration. Il codice controlla se il risultato della chiamata GetDiagnostics contiene qualsiasi diagnostica. In caso contrario, il codice genera la classe di modello di visualizzazione. Se invece il risultato contiene una raccolta di diagnostica, il codice mostra le informazioni per i dati diagnostici e restituisce null. La classe diagnostica fornisce informazioni molto granulari su ogni problema di codice; ad esempio, la proprietà Id restituisce un id di diagnostica. il metodo GetMessage restituisce il messaggio di diagnostico completa. GetLineSpan restituisce la posizione di diagnostica nel codice sorgente; e la proprietà Severity restituisce la gravità di diagnostica, ad esempio errore, avviso o informazioni.

Figura 5, il controllo per il codice problemi con le API di diagnostica

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using System;
namespace RoslynCore
{
  public static class ViewModelGeneration
  {
    public static SyntaxNode GenerateViewModel(SyntaxNode node)
    {
      // Find the first class in the syntax node
      var classNode =
        node.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault();
      if(classNode!=null)
      {
        var codeIssues = node.GetDiagnostics();
        if(!codeIssues.Any())
        {
          // Get the name of the model class
          var modelClassName = classNode.Identifier.Text;
          // The name of the ViewModel class
          var viewModelClassName = $"{modelClassName}ViewModel";
          // Only for demo purposes, pluralizing an object is done by
          // simply adding the "s" letter. Consider proper algorithms
          string newImplementation =
            $@"public class {viewModelClassName} : INotifyPropertyChanged
{{
public event PropertyChangedEventHandler PropertyChanged;
// Raise a property change notification
protected virtual void OnPropertyChanged(string propname)
{{
  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}}
private ObservableCollection<{modelClassName}> _{modelClassName}s;
public ObservableCollection<{modelClassName}> {modelClassName}s
{{
  get {{ return _{modelClassName}s; }}
  set
  {{
    _{modelClassName}s = value;
    OnPropertyChanged(nameof({modelClassName}s));
  }}
}}
public {viewModelClassName}() {{
// Implement your logic to load a collection of items
}}
}}
";
            var newClassNode =
              SyntaxFactory.ParseSyntaxTree(newImplementation).GetRoot()
              .DescendantNodes().OfType<ClassDeclarationSyntax>()
              .FirstOrDefault();
            // Retrieve the parent namespace declaration
            if(!(classNode.Parent is NamespaceDeclarationSyntax)) return null;
            var parentNamespace = (NamespaceDeclarationSyntax)classNode.Parent;
            // Add the new class to the namespace
            var newParentNamespace =
              parentNamespace.AddMembers(newClassNode).NormalizeWhitespace();
            return newParentNamespace;
          }
          else
          {
            foreach(Diagnostic codeIssue in codeIssues)
          {
            string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()},
              Location: {codeIssue.Location.GetLineSpan()},
              Severity: {codeIssue.Severity}";
            Console.WriteLine(issue);
          }
          return null;
        }
      }
      else
      {
        return null;
      }
    }
  }
}

A questo punto, se si introducano alcuni errori intenzionale del testo di origine contenuto nella variabile di modelli, all'interno del metodo GenerateSampleViewModel in Program.cs e quindi eseguire l'applicazione, sarà in grado di vedere come il compilatore c# restituisce informazioni complete su ogni problema di codice. Nella Figura 6 viene illustrato un esempio.

Rilevamento di problemi del codice con le API di diagnostica
Figura 6 individuazione di problemi del codice con le API di diagnostica

Vale la pena notare che il compilatore c# produce una struttura ad albero, anche se contiene diagnostica. Non solo non questo risultato in modo totalmente fedele con il testo di origine, fornisce inoltre agli sviluppatori un'opzione per risolvere tali problemi con i nuovi nodi di sintassi.

L'esecuzione di codice: di generare le API

Le API generano consentono per la compilazione di codice sorgente in assembly. Quindi, con la Reflection è possibile richiamare ed eseguire codice. Nell'esempio seguente è una combinazione di generazione di codice, creare e il rilevamento di diagnostica. Aggiungere un nuovo file denominato EmitDemo.cs al progetto, quindi prendere in considerazione il listato di codice illustrato nella figura 7. Come si può vedere, viene generato un SyntaxTree dal testo di origine che definisce una classe Helper contenente un metodo statico che calcola l'area del cerchio. L'obiettivo è generare un file DLL da questa classe ed eseguire il metodo CalculateCircleArea, passando il raggio come argomento.

Figura 7 compilazione ed esecuzione di codice con creazione di API e Reflection

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace RoslynCore
{
  public static class EmitDemo
  {
    public static void GenerateAssembly()
    {
      const string code = @"using System;
using System.IO;
namespace RoslynCore
{
 public static class Helper
 {
  public static double CalculateCircleArea(double radius)
  {
    return radius * radius * Math.PI;
  }
  }
}";
      var tree = SyntaxFactory.ParseSyntaxTree(code);
      string fileName="mylib.dll";
      // Detect the file location for the library that defines the object type
      var systemRefLocation=typeof(object).GetTypeInfo().Assembly.Location;
      // Create a reference to the library
      var systemReference = MetadataReference.CreateFromFile(systemRefLocation);
      // A single, immutable invocation to the compiler
      // to produce a library
      var compilation = CSharpCompilation.Create(fileName)
        .WithOptions(
          new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
        .AddReferences(systemReference)
        .AddSyntaxTrees(tree);
      string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
      EmitResult compilationResult = compilation.Emit(path);
      if(compilationResult.Success)
      {
        // Load the assembly
        Assembly asm =
          AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
        // Invoke the RoslynCore.Helper.CalculateCircleArea method passing an argument
        double radius = 10;
        object result = 
          asm.GetType("RoslynCore.Helper").GetMethod("CalculateCircleArea").
          Invoke(null, new object[] { radius });
        Console.WriteLine($"Circle area with radius = {radius} is {result}");
      }
      else
      {
        foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
        {
          string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()},
            Location: {codeIssue.Location.GetLineSpan()},
            Severity: {codeIssue.Severity}";
          Console.WriteLine(issue);
        }
      }
    }
  }
}

Nella prima parte, il codice crea una nuova compilazione che rappresenta una chiamata singola, non modificabile per il compilatore c#. L'oggetto CSharpCompilation consente la creazione di un assembly tramite il metodo crea e WithOptions consente di specificare il tipo di output per produrre, in questo caso DynamicallyLinkedLibrary. AddReferences viene utilizzato per aggiungere i riferimenti che del codice potrebbe essere necessario; a tale scopo, è necessario fornire un tipo che ha gli stessi riferimenti necessari per il codice. In questo caso particolare, è sufficiente creare gli stessi riferimenti su cui si basa il tipo di oggetto. Con GetTypeInfo(). Recuperare il nome dell'assembly per il riferimento Assembly.Location, MetadataReference.CreateFromFile crea quindi un riferimento all'assembly all'interno della compilazione. Alla fine, viene aggiunto l'albero della sintassi per la compilazione con AddSyntaxTrees.

Nella seconda parte del codice, una chiamata a CSharpCompilation.Emit tenta di generare il file binario e restituisce un oggetto di tipo EmitResult. Quest'ultimo è molto interessante: Espone una proprietà di successo di tipo booleano che indica se la compilazione ha avuto esito positivo ed espone inoltre una proprietà denominata diagnostica, che restituisce una matrice non modificabile di oggetti di diagnostica che possono essere molto utile per comprendere perché la compilazione non è riuscito. In figura 7, è possibile vedere facilmente come la proprietà di diagnostica viene eseguita l'iterazione se la compilazione non riesce. È importante ricordare che l'assembly di output è una libreria Standard di .NET, pertanto la compilazione di testo di origine avrà esito positivo solo se il codice analizzato con Roslyn si basa sulle API incluse in .NET Standard.

Ora vediamo cosa succede se la compilazione ha esito positivo. Lo spazio dei nomi System.Runtime.Loader, incluso nel pacchetto NuGet lo stesso nome importati all'inizio dell'articolo, espone una classe singleton denominata AssemblyLoadContext, che espone un metodo denominato LoadFromAssemblyPath. Questo metodo restituisce un'istanza della classe di Assembly, che consente di utilizzare la Reflection per ottenere innanzitutto un riferimento alla classe di supporto, quindi per ottenere un riferimento al metodo CalculateCircleArea, che è possibile richiamare passando un valore per il parametro radius. Il metodo MethodInfo. Invoke riceve null come primo argomento poiché CalculateCircleArea è un metodo statico. Pertanto, non occorre passare qualsiasi istanza del tipo. Se ora si chiama il metodo GenerateAssembly da Main in Program.cs, si noterà il risultato di queste operazioni, come illustrato in figura 8, dove il risultato del calcolo è visibile nella Console di Debug.

Il risultato della chiamata tramite Reflection del codice generato Roslyn
Figura 8, il risultato della chiamata tramite Reflection del codice generato Roslyn

Come è facile immaginare, creare API e Reflection in .NET Core offrono grande potenza e flessibilità, poiché è possibile generare, analizzare ed eseguire codice c#, indipendentemente dal sistema operativo. Infatti, tutti gli esempi illustrati in questo articolo verrà eseguito certamente non solo in Windows, ma anche la maggior parte delle distribuzioni Linux e macOS. Inoltre, la chiamata di codice da una libreria può essere eseguita mediante le API di Scripting Roslyn, pertanto non è limitata alla Reflection.

Conclusioni

.NET core consente di scrivere codice c# per creare applicazioni multipiattaforma che vengono eseguite su più sistemi operativi e dispositivi, e questo è perché i compilatori sono multipiattaforma. Roslyn, la piattaforma per compilatori .NET, conferisce il compilatore c# in .NET Core e consente agli sviluppatori di sfruttare le API per eseguire la compilazione, analisi e generazione di codice di analisi del codice completo. Ciò implica che è possibile automatizzare le attività tramite la generazione e l'esecuzione di codice in tempo reale, analizzare il testo di origine per problemi di codice ed eseguire un numero elevato di attività nel codice sorgente, in Windows, macOS e Linux.


Alessandro Del Soleè stato un MVP Microsoft dal 2008. L'assegnazione degli MVP di cinque volte l'anno, egli ha scritto molti libri, eBook, video didattici e articoli sullo sviluppo .NET con Visual Studio. Del Sole lavora come senior sviluppatore .NET, con particolare attenzione su .NET e lo sviluppo di app per dispositivi mobili, consulenza e formazione. È possibile seguirlo su Twitter: @progalex.

Grazie al seguente esperto tecnico Microsoft per la revisione dell'articolo: Dustin Campbell
Dustin Campbell è principal engineer presso Microsoft e un membro del team di progettazione di linguaggio c#. Dustin lavorando Roslyn dall'inizio ed è attualmente responsabile per l'estensione in c# per Visual Studio Code.