Comentarios de la documentación XML

Los archivos de origen de C# pueden tener comentarios estructurados que generan documentación de API para los tipos definidos en esos archivos. El compilador de C# genera un archivo XML que contiene datos estructurados que representan los comentarios y las firmas de API. Otras herramientas pueden procesar esa salida XML para crear documentación legible en forma de páginas web o archivos PDF, por ejemplo.

Este proceso proporciona muchas ventajas para agregar documentación de API en el código:

  • El compilador de C# combina la estructura del código de C# con el texto de los comentarios en un único documento XML.
  • El compilador de C# comprueba que los comentarios coinciden con las firmas de API para las etiquetas pertinentes.
  • Las herramientas que procesan los archivos de documentación XML pueden definir elementos y atributos XML específicos de esas herramientas.

Herramientas como Visual Studio proporcionan IntelliSense para muchos elementos XML comunes que se usan en los comentarios de documentación.

En este artículo se tratan los siguientes temas:

  • Comentarios de documentación y generación de archivos XML
  • Etiquetas validadas por el compilador de C# y Visual Studio
  • Formato del archivo XML generado

Creación de la salida de documentación XML

Para crear documentación para el código, escriba campos de comentario especiales indicados por tres barras diagonales. Los campos de comentario incluyen elementos XML que describen el bloque de código que sigue a los comentarios. Por ejemplo:

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

Establezca la opción GenerateDocumentationFile o DocumentationFile y el compilador encontrará todos los campos de comentario con etiquetas XML en el código fuente y creará un archivo de documentación XML a partir de esos comentarios. Cuando esta opción está habilitada, el compilador genera la advertencia CS1591 para cualquier miembro visible públicamente declarado en el proyecto sin comentarios de documentación XML.

Formatos de comentario XML

El uso de comentarios de documentos XML requiere delimitadores que indiquen dónde comienza y termina un comentario de documentación. Use los siguientes delimitadores con las etiquetas de documentación XML:

  • Delimitador de una sola línea ///: los ejemplos de documentación y las plantillas de proyecto de C# usan este formulario. Si hay un espacio en blanco después del delimitador, no se incluye en la salida XML.

    Nota

    Visual Studio inserta automáticamente las etiquetas <summary> y </summary>, y coloca el cursor dentro de estas etiquetas después de escribir el delimitador /// en el editor de código. Puede activar o desactivar esta característica en el cuadro de diálogo Opciones.

  • Delimitadores de varias líneas /** */: los delimitadores /** */ tienen las siguientes reglas de formato:
    • En la línea que contiene el delimitador /**, si el resto de la línea es un espacio en blanco, la línea no se procesa en busca de comentarios. Si el primer carácter después del delimitador /** es un espacio en blanco, dicho carácter de espacio en blanco se omite y se procesa el resto de la línea. En caso contrario, todo el texto de la línea situado después del delimitador /** se procesa como parte del comentario.

    • En la línea que contiene el delimitador */, si solo hay un espacio en blanco hasta el delimitador */, esa línea se omite. En caso contrario, el texto de la línea situado antes del delimitador */ se procesa como parte del comentario.

    • Para las líneas que aparecen después de la que comienza con el delimitador /**, el compilador busca un patrón común al principio de cada línea. El patrón puede consistir en un espacio en blanco opcional y un asterisco (*), seguido de otro espacio en blanco opcional. Si el compilador encuentra un patrón común al principio de cada línea que no comienza con el delimitador /** ni termina con el delimitador */, omite ese patrón para cada línea.

    • La única parte del comentario siguiente que se procesará es la línea que comienza con <summary>. Los tres formatos de etiquetas producen los mismos comentarios.

      /** <summary>text</summary> */
      
      /**
      <summary>text</summary>
      */
      
      /**
      * <summary>text</summary>
      */
      
    • El compilador identifica un patrón común de " * " al principio de la segunda y la tercera línea. El patrón no se incluye en la salida.

      /**
      * <summary>
      * text </summary>*/
      
    • El compilador no encuentra ningún patrón común en el comentario siguiente porque el segundo carácter de la tercera línea no es un asterisco. Todo el texto de la segunda y la tercera línea se procesa como parte del comentario.

      /**
      * <summary>
         text </summary>
      */
      
    • El compilador no encuentra ningún patrón en el comentario siguiente por dos razones. En primer lugar, el número de espacios antes del asterisco no es coherente. En segundo lugar, la quinta línea comienza con una pestaña, que no coincide con los espacios. Todo el texto de las líneas dos a cinco se procesa como parte del comentario.

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

Para hacer referencia a elementos XML (por ejemplo, la función procesa los elementos XML concretos que desea describir en un comentario de documentación XML), puede usar el mecanismo de entrecomillado estándar (&lt; y &gt;). Para hacer referencia a identificadores genéricos en elementos de referencia de código (cref), puede usar los caracteres de escape (por ejemplo, cref="List&lt;T&gt;") o llaves (cref="List{T}"). Como caso especial, el compilador analiza las llaves como corchetes angulares para que la creación del comentario de documentación resulte menos complicada al hacer referencia a identificadores genéricos.

Nota

Los comentarios de documentación XML no son metadatos; no se incluyen en el ensamblado compilado y, por tanto, no se puede obtener acceso a ellos mediante reflexión.

Herramientas que aceptan la entrada de documentación XML

Las siguientes herramientas crean la salida de los comentarios XML:

  • DocFX:DocFX es un generador de documentación de API para .NET, que actualmente admite C#, Visual Basic y F#. También permite personalizar la documentación de referencia generada. DocFX compila un sitio web HTML estático a partir del código fuente y los archivos Markdown. Además, DocFX proporciona la flexibilidad para personalizar el diseño y el estilo de su sitio web a través de plantillas. También puede crear plantillas personalizadas.
  • Sandcastle: las herramientas de Sandcastle crean archivos de ayuda para bibliotecas de clases administradas que contienen páginas de referencia tanto conceptuales como de API. Las herramientas de Sandcastle se basan en la línea de comandos y no tienen front-end de GUI, características de administración de proyectos ni proceso de compilación automatizado. El Generador de archivos de ayuda de Sandcastle proporciona herramientas independientes basadas en GUI y la línea de comandos para crear un archivo de ayuda de forma automatizada. También está disponible un paquete de integración de Visual Studio para que los proyectos de ayuda se puedan crear y administrar completamente desde Visual Studio.
  • Doxygen: Doxygen genera un explorador de documentación en línea (en HTML) o un manual de referencia fuera de línea (en LaTeX) a partir de un conjunto de archivos de código fuente documentados. También se admite la generación de resultados en RTF (MS Word), PostScript, PDF con hipervínculo, HTML comprimido, DocBook y páginas man de Unix. Puede configurar Doxygen para extraer la estructura de código de los archivos de código fuente no documentados.

Cadenas de identificador

Cada tipo o miembro se almacena en un elemento del archivo XML de salida. Cada uno de esos elementos tiene una cadena de identificador única que identifica el tipo o miembro. La cadena de identificador debe tener en cuenta los operadores, los parámetros, los valores devueltos, los parámetros de tipo genérico y los parámetros ref, in y out. Para codificar todos esos elementos potenciales, el compilador sigue reglas claramente definidas para generar las cadenas de identificador. Los programas que procesan el archivo XML usan la cadena de identificador para identificar el elemento de reflexión o de metadatos correspondiente de .NET al que se aplica la documentación.

El compilador cumple las siguientes reglas cuando genera las cadenas de identificador:

  • No hay ningún espacio en blanco en la cadena.

  • La primera parte de la cadena identifica el tipo de miembro mediante un carácter único seguido de dos puntos. Se utilizan los siguientes tipos de miembros:

    Carácter Tipo de miembro Notas
    N namespace No puede agregar comentarios de documentación a un espacio de nombres, pero sí puede hacer referencias cruzadas a ellos, en caso de que se admitan.
    T type Un tipo es una clase, una interfaz, una estructura, una enumeración o un delegado.
    F campo
    P propiedad Incluye indizadores u otras propiedades indizadas.
    M método Incluye métodos especiales, como constructores y operadores.
    E event
    ! cadena de error El resto de la cadena proporciona información sobre el error. El compilador de C# genera información de error para vínculos que no se puede resolver.
  • La segunda parte de la cadena es el nombre completo del elemento, que empieza por la raíz del espacio de nombres. El nombre del elemento, sus tipos envolventes y el espacio de nombres están separados por puntos. Si el nombre del elemento ya contiene puntos, estos se reemplazan por el signo hash ("#"). Se supone que ningún elemento tiene un signo hash directamente en su nombre. Por ejemplo, el nombre completo del constructor de String es "System.String.#ctor".

  • Para las propiedades y los métodos, se indica la lista de parámetros entre paréntesis. Si no hay ningún parámetro, tampoco habrá paréntesis. Los parámetros se separan mediante comas. La codificación de cada parámetro sigue directamente cómo se codifica en una firma de .NET (consulte Microsoft.VisualStudio.CorDebugInterop.CorElementType para obtener las definiciones de los elementos de todos los límites de la lista siguiente):

    • Tipos base. Los tipos normales (ELEMENT_TYPE_CLASS o ELEMENT_TYPE_VALUETYPE) se representan como el nombre completo del tipo.
    • Los tipos intrínsecos (por ejemplo, ELEMENT_TYPE_I4, ELEMENT_TYPE_OBJECT, ELEMENT_TYPE_STRING, ELEMENT_TYPE_TYPEDBYREF y ELEMENT_TYPE_VOID) se representan como el nombre completo del tipo completo correspondiente. Por ejemplo, System.Int32 o System.TypedReference.
    • ELEMENT_TYPE_PTR se representa como "*" después del tipo modificado.
    • ELEMENT_TYPE_BYREF se representa como "@" después del tipo modificado.
    • ELEMENT_TYPE_CMOD_OPT se representa como "!" y el nombre completo de la clase modificadora, después del tipo modificado.
    • ELEMENT_TYPE_SZARRAY se representa como "[]" después del tipo de elemento de la matriz.
    • ELEMENT_TYPE_ARRAY se representa como [límite inferior:size,límite inferior:size], donde el número de comas es la clasificación - 1, y los límites inferiores y el tamaño de cada dimensión, si se conocen, se representan en decimales. Si no se especifican un límite inferior ni un tamaño, se omiten. Si se omiten el límite inferior y el tamaño de una dimensión determinada, también se omite ":". Por ejemplo, una matriz de dos dimensiones con 1 como los límites inferiores y tamaños no especificados es [1:,1:].
  • Solo para los operadores de conversión (op_Implicit yop_Explicit), el valor devuelto del método se codifica como ~ seguido por el tipo de valor devuelto. Por ejemplo: <member name="M:System.Decimal.op_Explicit(System.Decimal arg)~System.Int32"> es la etiqueta del operador de conversión public static explicit operator int (decimal value); declarado en la clase System.Decimal.

  • Para tipos genéricos, el nombre del tipo está seguido de una tilde aguda y después de un número que indica el número de parámetros de tipo genérico. Por ejemplo: <member name="T:SampleClass``2"> es la etiqueta de un tipo que se define como public class SampleClass<T, U>. Para los métodos que toman tipos genéricos como parámetros, los parámetros de tipo genérico se especifican como números precedidos por tildes graves (por ejemplo, `0,`1). Cada número representa una notación de matriz de base cero para los parámetros genéricos del tipo.

    • ELEMENT_TYPE_PINNED se representa como "^" después del tipo modificado. El compilador de C# nunca genera esta codificación.
    • ELEMENT_TYPE_CMOD_REQ se representa como "|" y el nombre completo de la clase modificadora, después del tipo modificado. El compilador de C# nunca genera esta codificación.
    • ELEMENT_TYPE_GENERICARRAY se representa como "[?]" después del tipo de elemento de la matriz. El compilador de C# nunca genera esta codificación.
    • ELEMENT_TYPE_FNPTR se representa como "=FUNC:type(signature)", donde type es el tipo de valor devuelto y signature se corresponde con los argumentos del método. Si no hay ningún argumento, se omiten los paréntesis. El compilador de C# nunca genera esta codificación.
    • Los siguientes componentes de la firma no se representan porque nunca se usan para diferenciar métodos sobrecargados:
      • Convención de llamada
      • Tipo de valor devuelto
      • ELEMENT_TYPE_SENTINEL

En los ejemplos siguientes, se muestra cómo se generan las cadenas de identificador para una clase y sus miembros:

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() { return 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] { get { return 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) { return 1; }
    }
}

Especificación del lenguaje C#

Para obtener más información, consulte el anexo Especificación del lenguaje C# sobre los comentarios de documentación.