Enhancing Code Quality with Documentation in C#

Introduction

Documenting a project and documenting code are 2 different entities. It is not just about filling up code with comments; instead, it involves using a specific type of comments known as summaries. Documenting code enhances its readability and helps developer to understand purpose of code without diving into the actual code. Plus, these summaries are utilized by document generation applications to create external documents. Summaries are also supported by IntelliSense, developers can hover over a method or object name to display a summary that defines its purpose.

Out of all the perks, the one that has truly saved me is the power to remember my past deeds. When I revisit the code, reading the summary gives my forgetful brain a much-needed nudge, like a post-it note saying, "Hey, remember why you wrote this code, silly!"

Syntax

The summary is enclosed within triple forward slashes (///) and placed directly above the class, method, property, or any other code member.

/// <summary>
/// This is a summary
/// </summary>

A guide to write effective summaries

The thumb rule is to keep it short and clear. Explain what the code does and what problem it solves. The following are the various tags used in the summary.

<summary>

This is the main tag used to describe a block of code. It provides an overall summary or explanation of what the code does. In the following Listing 1, a class named Mobile is created with several properties such as Manufacturer, Model, and BatteryLevel. Additionally, there is a constant named MaxBatteryLevel and a static field called totalMobiles. By reading these summaries in the code, you can easily understand the purpose of the class and the nature of these fields.

/// <summary>
/// Represents a mobile device.
/// </summary>
public class Mobile
{
    /// <summary>
    /// The total number of mobile devices created.
    /// </summary>
    private static int totalMobiles;

    /// <summary>
    /// The maximum battery level of the mobile device.
    /// </summary>
    private const int MaxBatteryLevel = 100;

    /// <summary>
    /// The _manufacturer of the mobile device.
    /// </summary>
    private string _manufacturer;
    /// <summary>
    /// Gets or sets the manufacturer of the mobile device.
    /// </summary>
    public string Manufacturer
    {
        get => _manufacturer; 
        set { _manufacturer = value; }
    }

    /// <summary>
    /// The _model name of the mobile device.
    /// </summary>
    private string _model;
    /// <summary>
    /// Gets or sets the model name of the mobile device.
    /// </summary>
    public string Model
    {
        get => _model; 
        set { _model = value; }
    }

    /// <summary>
    /// The current battery level of the mobile device.
    /// </summary>
    private int _batteryLevel;
    /// <summary>
    /// Gets or sets the battery level of the mobile device.
    /// </summary>
    public int BatteryLevel
    {
        get => _batteryLevel; 
        set { _batteryLevel = value; }
    }
}

Listing 1: class Mobile with <summary> tags

<returns>

This tag is used in methods that return a value and specifies the expected result of the method.

/// <summary>
/// Gets the total number of mobile devices.
/// </summary>
/// <returns>The total number of mobile devices.</returns>
public static int GetTotalMobileCount()
{
    return totalMobiles;
}

Listing 2: Method GetTotalMobileCount() with <returns> tag

<param>

This tag is used in methods that accept parameters and explains the purpose or meaning of each parameter, helping developers understand how to use them correctly.

/// <summary>
/// Initializes a new instance of the <see cref="Mobile"/> class with the specified _manufacturer and _model.
/// </summary>
/// <param name="manufacturer">The _manufacturer of the mobile device.</param>
/// <param name="model">The _model of the mobile device.</param>
public Mobile(string manufacturer, string model)
{
    this._manufacturer = manufacturer;
    this._model = model;
    totalMobiles++;
}

Listing 3: Constructor of class Mobile accepting parameters using <param> tags

<exception>

  This tag is used in methods that are prone to exceptions, helping developers understand potential error scenarios and providing guidance on how to handle those exceptions.

/// <summary>
/// Charges the mobile device by increasing the battery level.
/// </summary>
/// <param name="chargingPercentage">The percentage of mobile battery to charge.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the charging percentage is less than or equal to zero.</exception>
/// <exception cref="InvalidOperationException">Thrown when the battery level exceeds the maximum level.</exception>
public void ChargeMobile(int chargingPercentage)
{
   if (chargingPercentage <= 0)
   {
       throw new InvalidOperationException("Charging percentage must be greater than zero.");
   }

    _batteryLevel += chargingPercentage;

    // Ensure the battery level does not exceed the maximum level.
    if (_batteryLevel > MaxBatteryLevel)
    {
        throw new InvalidOperationException("Battery level has reached the maximum level.");
    }
}

Listing 4: Method ChargeMobile() with <exception> tags

<example>

This tag is used to demonstrate the practical usage of a method, showcasing different scenarios and showing developers how to effectively utilize this method.

/// <summary>
/// Resets the battery level of the mobile device to zero.
/// </summary>
/// <example>
/// <code>
/// Mobile iPhone13= new Mobile("Apple", "iPhone13");
/// iPhone13.ChargeMobile(88);
/// Console.WriteLine(iPhone13.BatteryLevel); // Output: 88
/// iPhone13.ResetBatteryLevel();
/// Console.WriteLine(iPhone13.BatteryLevel); // Output: 0
/// </code>
/// </example>
public void ResetBatteryLevel()
{
    _batteryLevel = 0;
}

Listing 5:  Method ResetBatteryLevel() using <example> with <code> tag

<remarks>

This tag is used to provide additional comments or information that helps developers understand the code better. For example, it can explain validation rules or provide specific details about a property. In following listing 6, I've incorporated the remarks in the class Mobile and ResetBatteryLevel() method.

/// <summary>
...
/// </summary>
/// <remarks>
/// This class provides properties and methods to interact with a mobile device.
/// </remarks>
public class Mobile
{

    /// <summary>
    ...
    /// </summary>
    /// <remarks>
    /// This method should be used with caution as it will completely drain the battery of the mobile device.
    /// It is recommended to charge the device immediately after resetting the battery level to avoid complete shutdown.
    /// </remarks>
    public void ResetBatteryLevel()
    {
       _batteryLevel = 0;
    }    
}

Listing 6: class Mobile and method ResetBatteryLevel() with <remarks> tag

Before we explore the <seealso> tag, let's create a method named SendMessage() that combines all of the above tags, and then we can refer to this method using the <seealso> tag.

/// <summary>
/// Sends a message to the specified recipient.
/// </summary>
/// <param name="recipient">The recipient's phone number.</param>
/// <param name="message">The message to be sent.</param>
/// <returns>True if the message was sent successfully; else, false.</returns>
/// <exception cref="ArgumentNullException">Thrown when the recipient or message is null or empty.</exception>
/// <remarks>
/// This method uses the SMS service provider to send text messages.
/// </remarks>
/// <example>
/// <code>
/// Mobile iPhone14 = new Mobile("Apple", "iPhone14");
/// bool messageSent = iPhone14.SendMessage("1234567890", "sent from iphone");
/// if (messageSent)
/// {
///     Console.WriteLine("Message sent successfully.");
/// }
/// else
/// {
///    Console.WriteLine("Failed to send message.");
/// }
/// </code>
/// </example>
public bool SendMessage(string recipient, string message)
{
   bool isSent = false;
   if (string.IsNullOrEmpty(recipient))
   {
       throw new ArgumentNullException(nameof(recipient), "Recipient cannot be null or empty.");
   }

   if (string.IsNullOrEmpty(message))
   {
       throw new ArgumentNullException(nameof(message), "Message cannot be null or empty.");
   }

   // Magical code to send the text message and sets the value of isSent.
   return isSent;
}

Listing 6: Method SendMessage() with <summary>, <param>, <returns>, <exception>, <example>, <code> and <remarks> tags

<seealso>

This tag is used to provide references or links to other parts of the code, aiding in easier navigation for developers and allowing them to quickly access related code elements. It is important to note that the <seealso> tag should be written as <seealso cref=""/> to create a cross-reference to another code element.

/// <summary>
/// Sends an email to the specified recipient.
/// </summary>
/// <param name="recipient">The recipient's email address.</param>
/// <param name="subject">The subject of the email.</param>
/// <param name="body">The body content of the email.</param>
/// <seealso cref="SendMessage(string, string)"/>
public void SendEmail(string recipient, string subject, string body)
{
    // Magic code to send an email.
}

Listing 7: Method SendEmail() with <seealso> tag

IntelliSense

When you hover over a method call, you can view the complete summary description with proper sections. In the image below, you can see an example of me viewing the SendMessage() method. It displays the summary, returns, remarks, and exceptions tags information, providing a comprehensive overview of the method's purpose and behavior.

Figure 1: Summary tags information in tooltip

Conclusion

The summary tags in C# provide a powerful way to document your code and enhance its understandability. Clear and informative documentation helps developers understand your code faster, reduces errors, and promotes maintainability. By using these tags <summary>, <returns>, <param>, <exception>, <remarks>, <example>, and <seealso>, you can provide clear and concise documentation for classes, methods, properties, variables, and other code elements.

Remember to use the summary tags consistently throughout your codebase and keep your documentation up to date as your code evolves. Well-documented code not only benefits you but also contributes to the overall development team.


Similar Articles