Rediger

Del via


Documentation comments

C# source files can have structured comments that produce API documentation for the types defined in those files. The C# compiler produces an XML file that contains structured data representing the comments and the API signatures. Other tools can process that XML output to create human-readable documentation in the form of web pages or PDF files, for example.

This process provides many advantages for you to add API documentation in your code:

  • The C# compiler combines the structure of the C# code with the text of the comments into a single XML document.
  • The C# compiler verifies that the comments match the API signatures for relevant tags.
  • Tools that process the XML documentation files can define XML elements and attributes specific to those tools.

Tools like Visual Studio provide IntelliSense for many common XML elements used in documentation comments.

This article covers these topics:

  • Documentation comments and XML file generation
  • Tags validated by the C# compiler and Visual Studio
  • Format of the generated XML file

Create XML documentation output

You create documentation for your code by writing special comment fields indicated by triple slashes. The comment fields include XML elements that describe the code block that follows the comments. For example:

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

You set either the GenerateDocumentationFile or DocumentationFile option, and the compiler finds all comment fields with XML tags in the source code and creates an XML documentation file from those comments. When this option is enabled, the compiler generates the CS1591 warning for any publicly visible member declared in your project without XML documentation comments.

XML comment formats

The use of XML doc comments requires delimiters that indicate where a documentation comment begins and ends. You use the following delimiters with the XML documentation tags:

  • /// Single-line delimiter: The documentation examples and C# project templates use this form. If there's white space following the delimiter, it isn't included in the XML output.

    Note

    Visual Studio automatically inserts the <summary> and </summary> tags and positions your cursor within these tags after you type the /// delimiter in the code editor. You can turn this feature on or off in the Options dialog box.

  • /** */ Multiline delimiters: The /** */ delimiters have the following formatting rules:
    • On the line that contains the /** delimiter, if the rest of the line is white space, the line isn't processed for comments. If the first character after the /** delimiter is white space, that white-space character is ignored and the rest of the line is processed. Otherwise, the entire text of the line after the /** delimiter is processed as part of the comment.

    • On the line that contains the */ delimiter, if there's only white space up to the */ delimiter, that line is ignored. Otherwise, the text on the line up to the */ delimiter is processed as part of the comment.

    • For the lines after the one that begins with the /** delimiter, the compiler looks for a common pattern at the beginning of each line. The pattern can consist of optional white space and/or an asterisk (*), followed by more optional white space. If the compiler finds a common pattern at the beginning of each line that doesn't begin with the /** delimiter or end with the */ delimiter, it ignores that pattern for each line.

    • The only part of the following comment that's processed is the line that begins with <summary>. The three tag formats produce the same comments.

      /** <summary>text</summary> */
      
      /**
      <summary>text</summary>
      */
      
      /**
      * <summary>text</summary>
      */
      
    • The compiler identifies a common pattern of " * " at the beginning of the second and third lines. The pattern isn't included in the output.

      /**
      * <summary>
      * text </summary>*/
      
    • The compiler finds no common pattern in the following comment because the second character on the third line isn't an asterisk. All text on the second and third lines is processed as part of the comment.

      /**
      * <summary>
         text </summary>
      */
      
    • The compiler finds no pattern in the following comment for two reasons. First, the number of spaces before the asterisk isn't consistent. Second, the fifth line begins with a tab, which doesn't match spaces. All text from lines two through five is processed as part of the comment.

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

To refer to XML elements (for example, your function processes specific XML elements that you want to describe in an XML documentation comment), you can use the standard quoting mechanism (&lt; and &gt;). To refer to generic identifiers in code reference (cref) elements, you can use either the escape characters (for example, cref="List&lt;T&gt;") or braces (cref="List{T}"). As a special case, the compiler parses the braces as angle brackets to make the documentation comment less cumbersome to the author when referring to generic identifiers.

Note

The XML documentation comments are not metadata; they are not included in the compiled assembly and therefore they are not accessible through reflection.

Tools that accept XML documentation input

The following tools create output from XML comments:

  • DocFX: DocFX is an API documentation generator for .NET, which currently supports C#, Visual Basic, and F#. It also allows you to customize the generated reference documentation. DocFX builds a static HTML website from your source code and Markdown files. Also, DocFX provides you with the flexibility to customize the layout and style of your website through templates. You can also create custom templates.
  • Sandcastle: The Sandcastle tools create help files for managed class libraries containing both conceptual and API reference pages. The Sandcastle tools are command-line based and have no GUI front-end, project management features, or automated build process. The Sandcastle Help File Builder provides standalone GUI and command-line-based tools to build a help file in an automated fashion. A Visual Studio integration package is also available for it so that help projects can be created and managed entirely from within Visual Studio.
  • Doxygen: Doxygen generates an online documentation browser (in HTML) or an offline reference manual (in LaTeX) from a set of documented source files. There's also support for generating output in RTF (MS Word), PostScript, hyperlinked PDF, compressed HTML, DocBook, and Unix manual pages. You can configure Doxygen to extract the code structure from undocumented source files.

ID strings

Each type or member is stored in an element in the output XML file. Each of those elements has a unique ID string that identifies the type or member. The ID string must account for operators, parameters, return values, generic type parameters, ref, in, and out parameters. To encode all those potential elements, the compiler follows clearly defined rules for generating the ID strings. Programs that process the XML file use the ID string to identify the corresponding .NET metadata or reflection item that the documentation applies to.

The compiler observes the following rules when it generates the ID strings:

  • No white space is in the string.

  • The first part of the string identifies the kind of member using a single character followed by a colon. The following member types are used:

    Character Member type Notes
    N namespace You can't add documentation comments to a namespace, but you can make cref references to them, where supported.
    T type A type is a class, interface, struct, enum, or delegate.
    F field
    P property Includes indexers or other indexed properties.
    M method Includes special methods, such as constructors and operators.
    E event
    ! error string The rest of the string provides information about the error. The C# compiler generates error information for links that can't be resolved.
  • The second part of the string is the fully qualified name of the item, starting at the root of the namespace. The name of the item, its enclosing type(s), and namespace are separated by periods. If the name of the item itself has periods, they're replaced with the hash-sign ('#'). It's assumed that no item has a hash-sign directly in its name. For example, the fully qualified name of the String constructor is "System.String.#ctor".

  • For properties and methods, the parameter list enclosed in parentheses follows. If there are no parameters, no parentheses are present. The parameters are separated by commas. The encoding of each parameter follows directly how it's encoded in a .NET signature (See Microsoft.VisualStudio.CorDebugInterop.CorElementType for definitions of the all caps elements in the following list):

    • Base types. Regular types (ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE) are represented as the fully qualified name of the type.
    • Intrinsic types (for example, ELEMENT_TYPE_I4, ELEMENT_TYPE_OBJECT, ELEMENT_TYPE_STRING, ELEMENT_TYPE_TYPEDBYREF, and ELEMENT_TYPE_VOID) are represented as the fully qualified name of the corresponding full type. For example, System.Int32 or System.TypedReference.
    • ELEMENT_TYPE_PTR is represented as a '*' following the modified type.
    • ELEMENT_TYPE_BYREF is represented as a '@' following the modified type.
    • ELEMENT_TYPE_CMOD_OPT is represented as a '!' and the fully qualified name of the modifier class, following the modified type.
    • ELEMENT_TYPE_SZARRAY is represented as "[]" following the element type of the array.
    • ELEMENT_TYPE_ARRAY is represented as [lower bound:size,lower bound:size] where the number of commas is the rank - 1, and the lower bounds and size of each dimension, if known, are represented in decimal. If a lower bound or size isn't specified, it's omitted. If the lower bound and size for a particular dimension is omitted, the ':' is omitted as well. For example, a two-dimensional array with 1 as the lower bounds and unspecified sizes is [1:,1:].
  • For conversion operators only (op_Implicit and op_Explicit), the return value of the method is encoded as a ~ followed by the return type. For example: <member name="M:System.Decimal.op_Explicit(System.Decimal arg)~System.Int32"> is the tag for the cast operator public static explicit operator int (decimal value); declared in the System.Decimal class.

  • For generic types, the name of the type is followed by a backtick and then a number that indicates the number of generic type parameters. For example: <member name="T:SampleClass`2"> is the tag for a type that is defined as public class SampleClass<T, U>. For methods that take generic types as parameters, the generic type parameters are specified as numbers prefaced with backticks (for example `0,`1). Each number represents a zero-based array notation for the type's generic parameters.

    • ELEMENT_TYPE_PINNED is represented as a '^' following the modified type. The C# compiler never generates this encoding.
    • ELEMENT_TYPE_CMOD_REQ is represented as a '|' and the fully qualified name of the modifier class, following the modified type. The C# compiler never generates this encoding.
    • ELEMENT_TYPE_GENERICARRAY is represented as "[?]" following the element type of the array. The C# compiler never generates this encoding.
    • ELEMENT_TYPE_FNPTR is represented as "=FUNC:type(signature)", where type is the return type, and signature is the arguments of the method. If there are no arguments, the parentheses are omitted. The C# compiler never generates this encoding.
    • The following signature components aren't represented because they aren't used to differentiate overloaded methods:
      • calling convention
      • return type
      • ELEMENT_TYPE_SENTINEL

The following examples show how the ID strings for a class and its members are generated:

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 num, 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;
}

C# language specification

For more information, see the C# Language Specification annex on documentation comments.