Documentation du code C# en 1 min

Il est possible d’ajouter de la documentation dans du code C# et de générer cette documentation vers des fichiers XML à la compilation.
Il est souvent difficile de maintenir une documentation du code à jour car très souvent quand on modifie le code, on oublie de répercuter ces modifications dans la documentation. La documentation peut devenir alors assez vite obsolète et induire en erreur sur le fonctionnement réel du code.
Il existe en C# quelques balises qui permettent de garantir une certaine cohérence entre les éléments de documentation et le code.

Générer la documentation en fichiers XML

Paramétrer Visual Studio

Pour générer la documentation à la compilation en utilisant Visual Studio, il suffit d’activer l’option dans les propriétés d’un projet:

  1. Clique droit sur le projet puis cliquer “Properties” pour afficher les propriétés,
  2. Dans l’onglet “Build”,
  3. Dans la partie “Output”, cocher “XML documentation file”.

Par défaut le fichier contenant la documentation sera généré dans le répertoire de sortie, le plus souvent:

/bin/Debug

Pour que ce fichier contienne des informations, il faut explicitement documenter les objets dans le code C#.

Rendre les commentaires obligatoires

Dans Visual Studio, on peut considérer les “warning” comme des erreurs dans:

  1. Clique droit sur le projet puis cliquer “Properties” pour afficher les propriétés,
  2. Dans l’onglet “Build”,
  3. Dans la partie “Treat warnings as errors”, sélectionner “All”.

Avec ce paramétrage, tous les “warnings” provoqués par de la documentation incomplête ou incohérente à la compilation seront considérés comme des erreurs.

Documenter du code C#

D’une façon générale, pour ajouter de la documentation à un objet (classe, interface, propriété, méthode etc…) il suffit de taper dans Visual “///” au dessus de l’objet à documenter:

/// <summary>  
/// Commentaire pour la classes  
/// </summary>  
public class Class1  
{  
}

Pour une méthode:

/// <summary> 
/// Commentaire d'une méthode 
/// </summary> 
/// <param name="comments">Liste de commentaires</param> 
/// <param name="commentIndex">Index du commentaire</param> 
/// <returns>Commentaire</returns> 
public string GetCommentFromInt(List<string> comments, int commentIndex) 
{ 
}

La documentation est donc introduite à l’aide de:

  • <summary> pour le corps du commentaire,
  • <param> pour les arguments de méthodes,
  • <returns> pour le résultat de la méthode.

Ainsi la classe:

/// <summary> 
/// Commentaire pour la classes 
/// </summary> 
public class Class1 
{ 
    /// <summary> 
    /// Property in a class 
    /// </summary> 
    public int ClassProperty { get; private set; } 
 
    /// <summary> 
    /// Commentaire d'une méthode 
    /// </summary> 
    /// <param name="comments">Liste de commentaires</param> 
    /// <param name="commentIndex">Index du commentaire</param> 
    /// <returns>Commentaire</returns> 
    public string GetCommentFromInt(List<string> comments, int commentIndex) 
    { 
        return string.Empty; 
    } 
}

Générera la documentation:

<?xml version="1.0"?> 
<doc> 
    <assembly> 
        <name>CommentsInCode</name> 
    </assembly> 
    <members> 
        <member name="T:CommentsInCode.Class1"> 
            <summary> 
            Commentaire pour la classes 
            </summary> 
        </member> 
        <member name="M:CommentsInCode.Class1.GetCommentFromInt(System.Collections.Generic.List{System.String},System.Int32)"> 
            <summary> 
            Commentaire d'une méthode 
            </summary> 
            <param name="comments">Liste de commentaires</param> 
            <param name="commentIndex">Index du commentaire</param> 
            <returns>Commentaire</returns> 
        </member> 
        <member name="P:CommentsInCode.Class1.ClassProperty"> 
            <summary> 
            Property in a class 
            </summary> 
        </member> 
    </members> 
</doc>

Enrichir la documentation

Un des intérêts de la documentation de code est de pouvoir l’enrichir en ajoutant des références qui seront vérifiées à la compilation. Dans le cas où les références ne sont plus satisfaites, la compilation générera une erreur de compilation.

Référence vers un membre avec <see />

La balise <see cref="member" /> permet d’effectuer une référence vers un objet. Par exemple si on considère la classe:

public class Class1 
{ 
    public List<string> Comments { get; private set; } 
 
    /// <summary> 
    /// Commentaire d'une méthode 
    /// </summary> 
    /// <param name="commentIndex"></param> 
    /// <returns>Commentaire</returns> 
    public string GetCommentFromInt(int commentIndex) 
    { 
        return string.Empty; 
    } 
}

Dans les commentaires de l’argument commentIndex, on peut rajouter une référence vers le membre Class1.Comments en utilisant:

<see cref="Class1.Comments"/>

Ainsi:

/// <summary> 
/// Commentaire d'une méthode 
/// </summary> 
/// <param name="commentIndex">Index permettant de récupérer un commentaire 
/// dans <see cref="Class1.Comments"/></param> 
/// <returns>Commentaire</returns> 
public string GetCommentFromInt(int commentIndex) 
{ 
    return string.Empty; 
}

Dans le cas où la référence contient une erreur, par exemple:

<see cref="Class1.Coments"/>

On aura un warning ou une erreur de compilation (suivant la configuration des “warnings”):

XML comment on ... has cref attribute cref 'Class1.Coments' that could
    not be resolved

Quelques exemples pour ajouter ces références:

  • Référence vers une méthode: <see cref="Class1.GetCommentFromInt" />
  • Référence vers une méthode possédant plusieurs surcharges: il faut ajouter les arguments explicitement: <see cref="Class1.GetCommentFromInt(string,int)" />
  • Référence vers une fonction du framework: <see cref="String.Equals(string, string)" />.
  • Dans le cas où les namespaces ne sont pas indiqués, il faut préciser explicitement les namespaces de la méthode: <see cref="System.String.Equals(string, string)" />.

Cas particulier des génériques

Les génériques sont déclarés avec <T> ce qui peut poser problème car les caractères “<” et “>” sont interprétés en XML. La première solution consiste à remplacer le caractère “<” par &lt;.

Par exemple si on déclare la méthode:

public string GetCommentFromInt(List<string> comments, int commentIndex)

Une référence vers cette méthode s’écrira:

<see cref="Class1.GetCommentFromInt(List&lt;string>, int)" />

Une autre méthode consiste à remplacer les caractères “<” et “>” par respectivement “{” et “}”. En prenant le même exemple que précédemment, on aura:

<see cref="Class1.GetCommentFromInt(List{string}, int)" />

Balise <seealso />

Cette balise permet de rajouter une autre référence vers un objet quand une référence avec <see /> existe déjà. Elle s’utilise de la même façon que <see />.

Par exemple:

/// <summary> 
/// On peut obtenir une valeur avec <see cref="Class1.GetCommentFromInt(List{string}, int)" /> 
/// On peut aussi s'aider de <seealso cref="Class1.GetCommentFromInt(string, int)"/> 
/// </summary>

Balise <para />

Permet de structurer un texte en paragraphe lorsque ce texte se trouve entre des balises <summary/>, <remarks /> ou <returns/>.

Balise <exception />

Permet de documenter une exception. Par exemple, pour indiquer qu’une exception de type System.ArgumentNullException est levée si l’argument comments est nul:

/// <summary> 
///  
/// </summary> 
/// <param name="comments"></param> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
/// <exception cref="System.ArgumentNullException">Exception levée 
/// si <paramref name="comments"/> est nul.</exception> 
public string GetCommentFromInt(List<string> comments, int commentIndex)

Balises <c/> et <code />

Ces balises permettent de renvoyer du texte à interpréter sous forme de code:

  • <c/> doit être utilisé pour un élément sur une seule ligne et
  • <code /> doit être utilisé lorsque le code doit être affiché sur plusieurs lignes.

Par exemple pour <c/>:

/// <summary> 
/// Cette méthode permet de renvoyer <c>string.Empty</c>. 
/// </summary> 
/// <param name="commentFormat"></param> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
public string GetCommentFromInt(string commentFormat, int commentIndex) 
{ 
    return string.Empty; 
}

De même pour <code />:

/// <summary> 
/// Cette méthode permet de renvoyer <c>string.Empty</c>. Le corps de cette méthode est: 
/// <code>public string GetCommentFromInt(string commentFormat, int commentIndex) 
/// { 
///     return string.Empty; 
/// }</code>
/// </summary> 
/// <param name="commentFormat"></param> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
public string GetCommentFromInt(string commentFormat, int commentIndex) 
{ 
    return string.Empty; 
}

Balise <paramref />

Permet de faire référence au paramètre d’une méthode:

<paramref name="name"/>

Par exemple:

/// <summary> 
/// Le paramètre <paramref name="comments"/> est obligatoire. 
/// </summary> 
/// <param name="comments"></param> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
public string GetCommentFromInt(List<string> comments, int commentIndex)

Balise <remarks />

Ajoute des indications en plus de ce qui se trouve à l’intérieur d’une balise <summary />, par exemple:

/// <summary> 
/// Commentaire de base 
/// </summary> 
/// <param name="comments"></param> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
/// <remarks>Autre commentaire sur cette méthode</remarks>
public string GetCommentFromInt(List<string> comments, int commentIndex)

Balise <value />

S’utilise pour documenter des valeurs d’une propriété. Cette balise n’est pas rajoutée automatiquement.

Par exemple:

/// <summary> 
/// Renvoie l'index de la structure. 
/// </summary> 
/// <value>La valeur doit être un entier</value>
public int Index 
{ 
    get { return index; } 
    set { index = value; } 
}

Balise <typeparam />

Cette balise permet de documenter le type d’un générique. Elle est automatique ajoutée si la déclaration d’un objet contient des génériques.

Par exemple:

/// <summary> 
///  
/// </summary> 
/// <typeparam name="TReturn"></typeparam>
/// <typeparam name="TIndex"></typeparam>
/// <param name="commentIndex"></param> 
/// <returns></returns> 
public TReturn GetTypedComment<TReturn, TIndex>(TIndex commentIndex) 
{ 
    return default(TReturn); 
}

Balise <typeparamref />

Permet de faire une référence vers un type d’un générique.

Par exemple:

/// <summary> 
/// Le type <typeparamref name="TReturn"/> est le type de retour et  
/// <typeparamref name="TIndex"/> est le type de l'index. 
/// </summary> 
/// <typeparam name="TReturn"></typeparam> 
/// <typeparam name="TIndex"></typeparam> 
/// <param name="commentIndex"></param> 
/// <returns></returns> 
public TReturn GetTypedComment<TReturn, TIndex>(TIndex commentIndex)

Balise <list />

Permet d’indiquer une liste d’éléments. Le type de la liste doit être:

  • “bullet” pour une liste simple
  • “number” pour une liste numérotée
  • “table” pour un tableau.

Pour indiquer une liste, il faut utiliser la syntaxe:

<list type="bullet"> 
    <listheader> 
        <term>term</term> 
        <description>description</description> 
    </listheader> 
    <item> 
        <term>term</term> 
        <description>description</description> 
    </item> 
</list>

Les éléments sont utilisés de cette façon:

  • <listheader/> est utilisé dans le cas d’un tableau pour l’entête. Il peut être omis dans les autres cas. S’il y a plusieurs colonnes dans le tableau, il doit y avoir une succession de plusieurs éléments <listheader/>.
  • <term /> indique le nom d’un élément
  • <description /> indique la description d’une élément.
  • <item/> contient un élément à lister.

Autres balises

Pour avoir une liste complête des balises: Recommended Tags for Documentation Comments.

Outils générant la documentation à partir d’un fichier XML

Sur MSDN, on indique qu’il est possible d’utiliser NDoc ou SandCastle (sur GitHub ou CodePlex.

Pour avoir plus de détails pour utiliser SandCastle, on peut se référer à cette page (pour télécharger SandCastle, il faut utiliser le page CodePlex): Génération de documentation avec Sandcastle Help File Builder

D’autres solutions existent:

Références

Leave a Reply