Click or drag to resize

Tutorial

The instructions on this page are based on sample code that demonstrates how to define code examples, check for their successful execution, and generate text files containing both the examples' source code and their console outputs. Finally, it is showed how such text files can be exploited as the source of a code element in XML comments documenting a sample library.

The sample describing the intended work-flow for documenting by code examples is located here.

It includes what follows.

  • Project SampleClassLibrary, containing some code to be exemplified. The code is documented through XML comments including code examples generated via the Novacta.Documentation.CodeExamples library.

  • Project SampleClassLibrary.CodeExamples, a console application in which the code examples are defined. Note that this project references both SampleClassLibrary and Novacta.Documentation.CodeExamples.

  • Project SampleClassLibrary.Documentation, that generates the documentation for SampleClassLibrary exploiting the code examples. This last project can be successfully loaded by installing the Sandcastle Help File Builder.

Develop the code to be documented by examples

In this tutorial, project SampleClassLibrary contains the code that needs to be documented by examples. For instance, it includes class IntegerArrayOperation. The source code of this type is the following.

SampleClassLibrary\Advanced\IntegerArrayOperation.cs
using System;

namespace SampleClassLibrary.Advanced
{
    /// <summary>
    /// Provides a method to operate on arrays of integers.
    /// </summary>
    public static class IntegerArrayOperation
    {
        /// <summary>
        /// Applies the specified function to the given array of operands.
        /// </summary>
        /// <param name="func">The function to evaluate at each operand.</param>
        /// <param name="operands">The array of operands.</param>
        /// <returns>The results of the operations.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="func"/> is <b>null</b>.<br/>
        /// -or-<br/>
        /// <paramref name="operands"/> is <b>null</b>.
        /// </exception>
        /// <example>
        /// <para>
        /// In the following example, integers in a given array are squared 
        /// executing the <see cref="Operate(Func{int, int}, int[])"/> method.
        /// In addition, input validation is also checked.
        /// </para>
        /// <para>
        /// <code 
        /// language="cs" 
        /// source="..\SampleClassLibrary.CodeExamples\Advanced\IntegerArrayOperationExample.cs.txt"/>
        /// </para>
        /// </example>
        public static int[] Operate(Func<int, int> func, int[] operands)
        {
            if (func == null)
            {
                throw new ArgumentNullException(nameof(func));
            }
            if (operands == null)
            {
                throw new ArgumentNullException(nameof(operands));
            }

            int[] result = new int[operands.Length];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = IntegerOperation.Operate(func, operands[i]);
            }
            return result;
        }
    }

}

Here method Operate is documented through XML comments. Such comments include a code example, defined through the XML element

<code 
language="cs" 
source="..\SampleClassLibrary.CodeExamples\Advanced\IntegerArrayOperationExample.cs.txt"/>

Note how its source attribute is set to the path of a text file. In subsequent steps, you will learn how to generate such file via the Novacta.Documentation.CodeExamples library.

Define code examples

Create a console application

Code examples must be defined in a console application supporting the .NET Standard, version 2.0. In this tutorial, such application is represented by project SampleClassLibrary.CodeExamples.

Add to your console project a reference to the Novacta.Documentation.CodeExamples NuGet package, and a reference to the project containing the code to be exemplified. In this tutorial, this is SampleClassLibrary.

Define your code examples

Create a new example by defining a class that implements the ICodeExample interface. This is equivalent to implement method Main, that has no parameters and returns void.

Inside the body of such method, add the code you want exemplified. Use class SystemConsole to output the desired content for the example.

For instance, the following class define an example for type SampleClassLibrary.AdvancedIntegerArrayOperation.

SampleClassLibrary.CodeExamples\Advanced\IntegerArrayOperationExample.cs
using System;
using Novacta.Documentation.CodeExamples;
using SampleClassLibrary.Advanced;

namespace SampleClassLibrary.CodeExamples.Advanced
{
    /// <summary>
    /// An example showing how to exploit class <see cref="IntegerArrayOperation"/>.
    /// </summary>
    public class IntegerArrayOperationExample : ICodeExample
    {
        /// <summary>
        /// The method encapsulating the code to be exemplified.
        /// </summary>
        public void Main()
        {
            // Define an operator that squares its operand
            Func<int, int> square = (int operand) => operand * operand;

            // Define an array of operands
            int[] operands = new int[3] { 2, 4, 8 };

            // Operate on it
            int[] results = IntegerArrayOperation.Operate(square, operands);

            // Show results
            for (int i = 0; i < results.Length; i++)
            {
                Console.WriteLine(
                    "The result of squaring {0} is {1}.",
                    operands[i],
                    results[i]);
            }

            // Check that an operator cannot be null
            try
            {
                IntegerArrayOperation.Operate(null, new int[1]);
            }
            catch (Exception e)
            {
                Console.WriteLine();
                Console.WriteLine("Cannot apply a null function:");
                Console.WriteLine(e.Message);
            }

            // Check that an array of operands cannot be null
            try
            {
                IntegerArrayOperation.Operate(square, null);
            }
            catch (Exception e)
            {
                Console.WriteLine();
                Console.WriteLine("Cannot apply a function to a null array:");
                Console.WriteLine(e.Message);
            }

        }
    }
}

When the console application is executed, the console output of such example will be automatically captured and inserted, as a comment, in a text file also containing the example's source code, as follows.

SampleClassLibrary.CodeExamples\Advanced\IntegerArrayOperationExample.cs.txt
using System;
using SampleClassLibrary.Advanced;

namespace SampleClassLibrary.CodeExamples.Advanced
{
    public class IntegerArrayOperationExample  
    {
        public void Main()
        {
            // Define an operator that squares its operand
            Func<int, int> square = (int operand) => operand * operand;

            // Define an array of operands
            int[] operands = new int[3] { 2, 4, 8 };

            // Operate on it
            int[] results = IntegerArrayOperation.Operate(square, operands);

            // Show results
            for (int i = 0; i < results.Length; i++)
            {
                Console.WriteLine(
                    "The result of squaring {0} is {1}.",
                    operands[i],
                    results[i]);
            }

            // Check that an operator cannot be null
            try
            {
                IntegerArrayOperation.Operate(null, new int[1]);
            }
            catch (Exception e)
            {
                Console.WriteLine();
                Console.WriteLine("Cannot apply a null function:");
                Console.WriteLine(e.Message);
            }

            // Check that an array of operands cannot be null
            try
            {
                IntegerArrayOperation.Operate(square, null);
            }
            catch (Exception e)
            {
                Console.WriteLine();
                Console.WriteLine("Cannot apply a function to a null array:");
                Console.WriteLine(e.Message);
            }

        }
    }
}

// Executing method Main() produces the following output:
// 
// The result of squaring 2 is 4.
// The result of squaring 4 is 16.
// The result of squaring 8 is 64.
// 
// Cannot apply a null function:
// Value cannot be null.
// Parameter name: func
// 
// Cannot apply a function to a null array:
// Value cannot be null.
// Parameter name: operands

This is the text file exploited as the source of the <code> element documenting method Operate. Note how the references to the ICodeExample interface and the Novacta.Documentation.CodeExamples namespace are automatically deleted from the file.

Execute code examples

In your Program class, the implementation of method Main should resemble what follows.

SampleClassLibrary.CodeExamples\Program.cs
using Novacta.Documentation.CodeExamples;
using System;

namespace SampleClassLibrary.CodeExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            string codeBase = @"..\..\..\..\SampleClassLibrary.CodeExamples";
            string defaultNamespace = "SampleClassLibrary.CodeExamples";
            var analyzer = new CodeExamplesAnalyzer(
                codeBase,
                defaultNamespace);
            analyzer.Run();
            Console.ReadKey();
        }
    }
}

Such code instantiates an object of type CodeExamplesAnalyzer by passing to the constructor two pieces of information: the path of the folder containing the examples' source code files, and the default namespace of the console application where the examples are defined: these are returned by properties CodeBase and DefaultNamespace, respectively. Finally, method Run is executed. This will look for code examples, execute them individually, and capture their console outputs. It will also search for the corresponding source code files, assuming that the following conditions hold true.

  • Each type implementing ICodeExample is defined in a C# file named from the type name.

  • Types in the DefaultNamespace have their source code files stored in the CodeBase folder.

  • Source code files of types in nested namespaces are stored in a directory tree under the CodeBase folder, reflecting the project namespace hierarchy.

If a code example is executed successfully and its source code can be found, a text file is created that can be used as the source of <code> XML elements in C# documentation comments.

When the application is executed, its console output shows a report in which the examples are listed, signaling if any source code file cannot be found, or any execution is not successful. For instance, by executing the sample application defined in project SampleClassLibrary.CodeExamples, the following is reported.

Console