Commenti sulla documentazione

I file di origine C# possono avere commenti strutturati che producono documentazione API per i tipi definiti in tali file. Il compilatore C# genera un file XML che contiene dati strutturati che rappresentano i commenti e le firme API. Altri strumenti possono elaborare l'output XML per creare una documentazione leggibile, ad esempio sotto forma di pagine Web o file PDF.

Questo processo offre molti vantaggi per aggiungere la documentazione API nel codice:

  • Il compilatore C# combina la struttura del codice C# con il testo dei commenti in un singolo documento XML.
  • Il compilatore C# verifica che i commenti corrispondano alle firme API per i tag pertinenti.
  • Gli strumenti che elaborano i file di documentazione XML possono definire elementi e attributi XML specifici per tali strumenti.

Strumenti come Visual Studio forniscono IntelliSense per molti elementi XML comuni usati nei commenti relativi alla documentazione.

Questo articolo illustra i seguenti argomenti:

  • Commenti relativi alla documentazione e generazione di file XML
  • Tag convalidati dal compilatore C# e da Visual Studio
  • Formato del file XML generato

Creare output con documentazione XML

È possibile creare la documentazione per il codice scrivendo campi di commento speciali indicati da barre triple. I campi di commento includono elementi XML che descrivono il blocco di codice che segue i commenti. Ad esempio:

/// <summary>
/// This class performs an important function.
/// </summary>
public class MyClass { }

È possibile impostare l'opzione GenerateDocumentationFile o DocumentationFile e il compilatore trova tutti i campi di commento con tag XML nel codice sorgente e crea un file di documentazione XML da tali commenti. Quando questa opzione è abilitata, il compilatore genera l'avviso CS1591 per qualsiasi membro visibile pubblicamente dichiarato nel progetto senza commenti in formato documentazione XML.

Formati di commento XML

L'uso dei commenti in formato documentazione XML relativi alla documentazione richiede la specifica di delimitatori che indichino il punto di inizio e di fine di un commento relativo alla documentazione. Usare i delimitatori seguenti con i tag della documentazione XML:

  • /// Delimitatore a riga singola: gli esempi di documentazione e i modelli di progetto C# utilizzano questo modulo. Se lo spazio vuoto segue il delimitatore, esso non è incluso nell'output XML.

    Nota

    Visual Studio inserisce automaticamente i tag <summary> e </summary> e posiziona il cursore all'interno di questi tag dopo aver digitato il delimitatore ///nell'editor del codice. È possibile attivare o disattivare questa funzionalità nella finestra di dialogo Opzioni.

  • /** */ Delimitatori su più righe: i delimitatori /** */ hanno le regole di formattazione seguenti:
    • Nella riga contenente il delimitatore /**, se la parte restante della riga è rappresentata da uno spazio vuoto, la riga non viene elaborata per i commenti. Se il primo carattere dopo il delimitatore /** è uno spazio vuoto, lo spazio vuoto viene ignorato e il resto della riga viene elaborato. In caso contrario, l'intero testo della riga dopo il delimitatore /** viene elaborato come parte del commento.

    • Se sulla riga contenente il delimitatore */ sono presenti solo spazi vuoti fino al delimitatore */, la riga viene ignorata. In caso contrario, il testo nella riga fino al delimitatore */ viene elaborato come parte del commento.

    • Per le righe successive a quella che inizia con il delimitatore /**, il compilatore cerca un modello comune all'inizio di ogni riga. Il modello può essere costituito da uno spazio vuoto facoltativo e/o un asterisco (*) seguiti da altri spazi vuoti facoltativi. Se trova un modello comune all'inizio di ogni riga che non inizia con il delimitatore /** o finisce con il delimitatore */, il compilatore ignora tale modello per ogni riga.

    • La sola parte del commento riportato di seguito che viene elaborata è la riga che inizia con <summary>. I tre formati di tag producono gli stessi commenti.

      /** <summary>text</summary> */
      
      /**
      <summary>text</summary>
      */
      
      /**
      * <summary>text</summary>
      */
      
    • Il compilatore identifica il modello comune " * " all'inizio della seconda e della terza riga. Il modello non è incluso nell'output.

      /**
      * <summary>
      * text </summary>*/
      
    • Il compilatore non trova alcun modello comune nel commento seguente poiché il secondo carattere nella terza riga non è un asterisco. Tutto il testo contenuto nella seconda e nella terza riga viene elaborato come parte del commento.

      /**
      * <summary>
         text </summary>
      */
      
    • Nel commento seguente il compilatore non rileva alcun modello per due motivi. In primo luogo, il numero di spazi prima dell'asterisco non è coerente. In secondo luogo, la quinta riga inizia con una tabulazione senza corrispondenza degli spazi. Tutto il testo dalla seconda alla quinta riga verrà elaborato come parte del commento.

      /**
        * <summary>
        * text
      *  text2
       	*  </summary>
      */
      

Per fare riferimento agli elementi XML (ad esempio, la funzione elabora elementi XML specifici che si desidera descrivere in un commento della documentazione XML), è possibile utilizzare il meccanismo standard (&lt; e &gt;). Per fare riferimento agli identificatori generici in elementi di riferimento di codice (cref), è possibile usare caratteri di escape, ad esempio cref="List&lt;T&gt;", o parentesi graffe (cref="List{T}"). Come caso particolare, il compilatore analizza le parentesi graffe come parentesi uncinate per rendere il commento relativo alla documentazione meno complesso da creare quando viene fatto riferimento a identificatori generici.

Nota

I commenti relativi alla documentazione XML non sono metadati, ovvero non vengono inclusi nell'assembly compilato e pertanto non sono accessibili mediante reflection.

Strumenti che accettano l'input della documentazione XML

Gli strumenti seguenti creano l'output dai commenti XML:

  • DocFX: DocFX è un generatore di documentazione API per .NET, che attualmente supporta C#, Visual Basic e F#. Consente inoltre di personalizzare la documentazione di riferimento generata. DocFX compila un sito Web HTML statico dal codice sorgente e dai file Markdown. Inoltre, DocFX offre la flessibilità necessaria per personalizzare il layout e lo stile del sito Web tramite dei modelli. È inoltre possibile creare modelli personalizzati.
  • Sandcastle: glistrumenti Sandcastle creano file di supporto per le librerie di classi gestite contenenti sia pagine di riferimento concettuali sia API. Gli strumenti Sandcastle sono basati sulla riga di comando e non dispongono di funzionalità di gestione dei progetti GUI (interfaccia utente grafica) front-end o di processo di compilazione automatizzato. Sandcastle Help File Builder fornisce l'interfaccia utente grafica autonoma e gli strumenti basati sulla riga di comando per creare un file di supporto in modo automatizzato. È disponibile anche un pacchetto di integrazione di Visual Studio, in modo che i progetti di supporto possano essere creati e gestiti interamente da Visual Studio.
  • Doxygen: Doxygen genera un browser di documentazione online (in HTML) o un manuale di riferimento offline (in LaTeX) da un set di file di origine documentati. È disponibile anche il supporto per la generazione di output nelle pagine di manuale RTF (MS Word), PostScript, PDF con collegamento ipertestuale, HTML compresso, DocBook e Unix. È possibile configurare Doxygen per estrarre la struttura del codice dai file di origine non documentati.

Stringhe ID

Ogni tipo o membro viene archiviato in un elemento nel file XML di output. Ognuno di questi elementi è dotato di una stringa ID univoca che identifica il tipo o il membro. La stringa ID deve tenere conto di operatori, parametri, valori restituiti, parametri di tipo generico, ref, in e out. Per codificare tutti questi elementi potenziali, il compilatore segue regole chiaramente definite per generare le stringhe ID. I programmi che elaborano il file XML utilizzano la stringa ID per identificare il corrispondente elemento metadati o reflection di .NET a cui si applica la documentazione.

Per generare gli ID, il compilatore applica le regole seguenti:

  • Assenza di spazi vuoti nella stringa.

  • La prima parte della stringa identifica il tipo di membro utilizzando un singolo carattere seguito da due punti. Vengono usati i tipi di membri seguenti:

    Carattere Tipo di membro Note
    N namespace Non è possibile aggiungere a uno spazio dei nomi commenti relativi alla documentazione, ma, se supportati, è possibile creare riferimenti cref a tali commenti.
    T type Un tipo è una classe, un'interfaccia, uno struct, un'enumerazione o un delegato.
    F campo
    P proprietà Include indicizzatori o altre proprietà indicizzate.
    M metodo Include metodi speciali, ad esempio costruttori e operatori.
    E evento
    ! stringa di errore Nella parte restante della stringa vengono fornite informazioni sull'errore. Il compilatore C# genera informazioni sugli errori per tutti i collegamenti che non è possibile risolvere.
  • La seconda parte della stringa identifica il nome completo dell'elemento, a partire dalla radice dello spazio dei nomi. Il nome dell'elemento, i tipi di inclusione e lo spazio dei nomi sono separati da punti. Se il nome dell'elemento contiene dei punti, questi verranno sostituiti con il segno di cancelletto ('#'), in base al presupposto che nessun nome di elemento contiene direttamente tale segno. Ad esempio, il nome completo del costruttore String è "System.String.#ctor".

  • Per le proprietà e i metodi, segue l'elenco dei parametri racchiuso tra parentesi. Se non sono presenti parametri, non sono presenti parentesi. I parametri sono separati da virgole. La codifica di ogni parametro segue direttamente il modo in cui viene codificata in una firma .NET (vedere Microsoft.VisualStudio.CorDebugInterop.CorElementType per le definizioni di tutti gli elementi in maiuscolo riportati nell'elenco seguente):

    • Tipi di base. I tipi regolari (ELEMENT_TYPE_CLASS o ELEMENT_TYPE_VALUETYPE) sono rappresentati come nome completo del tipo.
    • I tipi intrinseci (ad esempio ELEMENT_TYPE_I4, ELEMENT_TYPE_OBJECT, ELEMENT_TYPE_STRING, ELEMENT_TYPE_TYPEDBYREF e ELEMENT_TYPE_VOID) sono rappresentati come nome completo del tipo completo corrispondente. Ad esempio, System.Int32 o System.TypedReference.
    • ELEMENT_TYPE_PTR viene rappresentato con '*' dopo il tipo modificato.
    • ELEMENT_TYPE_BYREF viene rappresentato con '@' dopo il tipo modificato.
    • ELEMENT_TYPE_CMOD_OPT viene rappresentato con '!' seguito dal nome completo della classe di modificatori dopo il tipo modificato.
    • ELEMENT_TYPE_SZARRAY viene rappresentato con "[]" dopo il tipo di elemento della matrice.
    • ELEMENT_TYPE_ARRAY viene rappresentato con [limite inferiore: size,limite inferiore:size], dove il numero di virgole indica il numero di dimensioni - 1. I limiti inferiori e le dimensioni di ogni dimensione, qualora noti, vengono rappresentati con valori decimali. Se non viene specificato un limite o una dimensione inferiore, viene omesso. Se vengono omessi il limite inferiore e la dimensione per una dimensione specifica, viene omesso anche ':'. Ad esempio, una matrice a due dimensioni con limiti inferiori pari a 1 e dimensioni non specificate viene rappresentata con [1:,1:].
  • Limitatamente agli operatori di conversione (op_Implicit and op_Explicit), il valore restituito del metodo viene codificato con ~ seguito dal tipo restituito, codificato. Ad esempio: <member name="M:System.Decimal.op_Explicit(System.Decimal arg)~System.Int32"> è il tag per l'operatore cast public static explicit operator int (decimal value); dichiarato nella classe System.Decimal.

  • Nel caso di tipi generici, il nome del tipo è seguito da un apice inverso e quindi da un numero che indica il numero di parametri di tipo generici. Ad esempio: <member name="T:SampleClass`2"> è il tag di un tipo che viene definito come public class SampleClass<T, U>. Nel caso di metodi che accettano tipi generici come parametri, i parametri dei tipi generici sono caratterizzati da numeri preceduti da apici inversi (ad esempio `0,`1). Ogni numero rappresenta la notazione della matrice in base zero per i parametri generici del tipo.

    • ELEMENT_TYPE_PINNED è rappresentato con '^' dopo il tipo modificato. Il compilatore C# non genera mai tale codifica.
    • ELEMENT_TYPE_CMOD_REQ viene rappresentato con '|' seguito dal nome completo della classe di modificatori dopo il tipo modificato. Il compilatore C# non genera mai tale codifica.
    • ELEMENT_TYPE_GENERICARRAY viene rappresentato con "[?]" dopo il tipo di elemento della matrice. Il compilatore C# non genera mai tale codifica.
    • ELEMENT_TYPE_FNPTR viene rappresentato con "=FUNC:type(signature)", dove type rappresenta il tipo restituito e signature identifica gli argomenti del metodo. Se non vi sono argomenti, le parentesi vengono omesse. Il compilatore C# non genera mai tale codifica.
    • I componenti della firma seguenti non vengono rappresentati perché non sono utilizzati per distinguere i metodi di overload:
      • convenzione di chiamata
      • tipo restituito
      • ELEMENT_TYPE_SENTINEL

Negli esempi seguenti viene illustrato come vengono generate le stringhe ID per una classe e i relativi membri:

namespace MyNamespace;

/// <summary>
/// Enter description here for class X.
/// ID string generated is "T:MyNamespace.MyClass".
/// </summary>
public unsafe class MyClass
{
    /// <summary>
    /// Enter description here for the first constructor.
    /// ID string generated is "M:MyNamespace.MyClass.#ctor".
    /// </summary>
    public MyClass() { }

    /// <summary>
    /// Enter description here for the second constructor.
    /// ID string generated is "M:MyNamespace.MyClass.#ctor(System.Int32)".
    /// </summary>
    /// <param name="i">Describe parameter.</param>
    public MyClass(int i) { }

    /// <summary>
    /// Enter description here for field Message.
    /// ID string generated is "F:MyNamespace.MyClass.Message".
    /// </summary>
    public string? Message;

    /// <summary>
    /// Enter description for constant PI.
    /// ID string generated is "F:MyNamespace.MyClass.PI".
    /// </summary>
    public const double PI = 3.14;

    /// <summary>
    /// Enter description for method Func.
    /// ID string generated is "M:MyNamespace.MyClass.Func".
    /// </summary>
    /// <returns>Describe return value.</returns>
    public int Func() => 1;

    /// <summary>
    /// Enter description for method SomeMethod.
    /// ID string generated is "M:MyNamespace.MyClass.SomeMethod(System.String,System.Int32@,System.Void*)".
    /// </summary>
    /// <param name="str">Describe parameter.</param>
    /// <param name="num">Describe parameter.</param>
    /// <param name="ptr">Describe parameter.</param>
    /// <returns>Describe return value.</returns>
    public int SomeMethod(string str, ref int nm, void* ptr) { return 1; }

    /// <summary>
    /// Enter description for method AnotherMethod.
    /// ID string generated is "M:MyNamespace.MyClass.AnotherMethod(System.Int16[],System.Int32[0:,0:])".
    /// </summary>
    /// <param name="array1">Describe parameter.</param>
    /// <param name="array">Describe parameter.</param>
    /// <returns>Describe return value.</returns>
    public int AnotherMethod(short[] array1, int[,] array) { return 0; }

    /// <summary>
    /// Enter description for operator.
    /// ID string generated is "M:MyNamespace.MyClass.op_Addition(MyNamespace.MyClass,MyNamespace.MyClass)".
    /// </summary>
    /// <param name="first">Describe parameter.</param>
    /// <param name="second">Describe parameter.</param>
    /// <returns>Describe return value.</returns>
    public static MyClass operator +(MyClass first, MyClass second) { return first; }

    /// <summary>
    /// Enter description for property.
    /// ID string generated is "P:MyNamespace.MyClass.Prop".
    /// </summary>
    public int Prop { get { return 1; } set { } }

    /// <summary>
    /// Enter description for event.
    /// ID string generated is "E:MyNamespace.MyClass.OnHappened".
    /// </summary>
    public event Del? OnHappened;

    /// <summary>
    /// Enter description for index.
    /// ID string generated is "P:MyNamespace.MyClass.Item(System.String)".
    /// </summary>
    /// <param name="str">Describe parameter.</param>
    /// <returns></returns>
    public int this[string s] => 1;

    /// <summary>
    /// Enter description for class Nested.
    /// ID string generated is "T:MyNamespace.MyClass.Nested".
    /// </summary>
    public class Nested { }

    /// <summary>
    /// Enter description for delegate.
    /// ID string generated is "T:MyNamespace.MyClass.Del".
    /// </summary>
    /// <param name="i">Describe parameter.</param>
    public delegate void Del(int i);

    /// <summary>
    /// Enter description for operator.
    /// ID string generated is "M:MyNamespace.MyClass.op_Explicit(MyNamespace.MyClass)~System.Int32".
    /// </summary>
    /// <param name="myParameter">Describe parameter.</param>
    /// <returns>Describe return value.</returns>
    public static explicit operator int(MyClass myParameter) => 1;
}

Specifiche del linguaggio C#

Per altre informazioni, vedere l'allegato Specifica del linguaggio C# nei commenti relativi alla documentazione.