inheritdoc

This element can help minimize the effort required to document complex APIs by allowing common documentation to be inherited from base types/members.

  Note

Prior to Visual Studio 2019 version 16.4, this was a custom XML comments element implemented by Sandcastle and the Sandcastle Help File Builder. It does not appear in the list of IntelliSense elements for XML comments prior to that release.

Syntax

Although typically used as a top-level element, it can be used as an inline element as well. The use of the element by itself on a member is enough to satisfy the compiler so that it will not issue warnings about missing comments on public members. Using it in conjunction with other elements allows you to inherit common information such as value and parameter descriptions while overriding the inherited documentation for other elements such as summary and remarks. Documentation can be inherited from any member from classes within your own assemblies or from members of other assemblies as well as the base .NET Framework class library. The syntax of the element is as follows:

 
<inheritdoc [cref="member"] [path="xpath-filter-expr"] />

  Important

As of November 2019, the select attribute has been deprecated. Use the equivalent path attribute instead which is consistent with the XML comments IntelliSense usage in Visual Studio 2019 and later.

The optional cref attribute overrides the standard search method to allow documentation inheritance from an alternate user-specified member indicated by the member value.

The optional path attribute applies the specified XPath filter expression to the inherited comments. This is useful if you want to limit the inherited documentation to a specific subset of elements or just select a particular instance or set of comments. The expression can be any valid XPath query that will result in a node set.

By making use of the cref and path attributes either by themselves or together, you can fine tune the inheritance of documentation. You can also nest the element within other elements to further refine the level of inheritance.

When using the Sandcastle Help File Builder, its GenerateInheritedDocumentation tool handles the task of generating the inherited documentation. The following documentation is based on the Sandcastle Help File Builder's implementation.

Top-Level Inheritance Rules

The inheritdoc element is valid at the root level (i.e. the same level as summary elements) on types, interfaces, virtual members, interface member implementations, and constructors. Its use on any other member type will result in no comments being inherited unless a cref attribute is specified. Note that the element is also valid in project summary and namespace summary comments as long as a cref attribute is specified to indicate from where to inherit the comments. When specified at the root level in a set of XML comments, the documentation search is performed as follows:

  • If an explicit cref attribute is specified, the documentation from the specified namespace/type/member is inherited. If a cref attribute is not specified, the following rules apply.

  • For types and interfaces:

    • Inherit documentation from all base classes working backwards up the inheritance chain.

    • Inherit documentation from all interface implementations (if any) working through them in the order listed in the reflection information file (usually alphabetically).

  • For constructors:

    • Search backwards up the type inheritance chain for a constructor with a matching signature.

    • If a match is found, its documentation is inherited.

  • For virtual members and interface implementations:

    • If the member is an override, documentation is inherited from the member it overrides.

    • If the member is part of an interface, documentation is inherited from the interface member being implemented.

  • Explicit interface implementations will automatically inherit documentation from the interface member that they implement if no documentation is supplied by the user. This is done automatically because these members are by definition private and the compiler will not issue a warning if the user does not supply documentation. As such, you can omit the inheritdoc element from them unless you want to customize the comments.

  • With or without an explicit cref attribute, if the inherited documentation itself contains inheritdoc elements, they will be expanded recursively working backwards up the inheritance chain.

  • In all cases, if a path attribute is present, it is used to filter the inherited comments based on the specified XPath query.

When inheriting documentation at the root level, if the following elements already exist in the member's comments, the inherited versions are ignored:

  • example

  • exclude

  • filterpriority

  • preliminary

  • summary

  • remarks

  • returns

  • threadsafety

  • value

The overloads element will never be inherited. This prevents the doubling of comments on the overloads page. However, you can inherit the contents of the overloads element using a path attribute with a value of "overloads/*". See the example below in the Examples section that inherits the elements from an overloads element. If the element only contains text and you want to inherit that text, include an overloads element with a nested inheritdoc element. For example:

 
/// <inheritdoc /> 
/// 
/// <overloads>
/// The inheritdoc element outside this "overloads" element inherits the standard
/// summary, parameters, remarks, etc. for the member itself.
/// 
/// The inheritdoc element below will inherit the overload text from the implemented
/// member and it will appear on the overloads page:
/// 
/// <inheritdoc /> 
/// 
/// </overloads>

All other elements will be inherited unless they match an element by the same name that contains a cref, href, name, vref, or xref attribute with an identical value in the member's comments. To merge comments in one of the above elements from one or more sources, use one or more nested inheritdoc elements within the given element. See below for examples.

Be aware that when param elements are inherited, the parameter's name in your class's member must match the base member's parameter name. If they do not match, you will not see any inherited documentation for the parameter. Also, if you supply comments for one parameter but omit comments for other parameters in order to inherit their documentation from a base implementation, the compiler will issue a warning. In this case, you can use a #pragma warning directive to disable the warning temporarily or add it to the project settings to disable the warning globally. See below for an example.

Inline Inheritance Rules

The inheritdoc element can also be nested within other XML comments elements such as summary, remarks, example etc. in order to inherit specific parts of the documentation within those elements. When nested, the same root level inheritance rules apply and will be used to locate the first member with comments from which to inherit documentation. In addition, a filter will be automatically included based on the parent element or elements within which the inheritdoc element is nested. The cref and path attributes can also be applied to further qualify how the documentation is inherited. If you do not want to have the parent elements automatically included in the filter, you must supply a path attribute with a rooted XPath query that specifies from where to obtain the comments (i.e. path="/summary/node()").

Additional Comment File Sources and IntelliSense

In the Sandcastle Help File Builder, additional sources of inherited documentation (i.e. comments from third party class libraries) can be added to the Documentation Sources project node. This allows you to inherit documentation from base class libraries without having to add them as documented assemblies in your project.

Since the XML comments produced by the compiler are incomplete when using inheritdoc, it is highly recommended that you make use of the IntelliSense Build Component to produce an IntelliSense XML comments file. It will include the fully expanded set of inherited documentation so that Visual Studio can provide useful and accurate API help in the code editor and object browser.

Examples

The following show various examples of using the inheritdoc element. See the comments within each for details about what the examples are showing.

Constructor Documentation Inheritance
/// <summary>
/// This exception class is thrown by the application if it encounters an
/// unrecoverable error.
/// </summary>
/// <conceptualLink target="86453FFB-B978-4A2A-9EB5-70E118CA8073" />
[Serializable]
public class CustomException : Exception
{
    /// <summary>
    /// Default constructor.
    /// </summary>
    /// <overloads>There are four overloads for the constructor</overloads>
    public CustomException()
    {
    }

    /// <inheritdoc />
    public CustomException(string message) : base(message)
    {
        // Inherit documentation from the base Exception class matching
        // this constructor's signature.
    }

    /// <inheritdoc />
    public CustomException(string message, Exception innerException) :
      base(message, innerException)
    {
        // Inherit documentation from the base Exception class matching
        // this constructor's signature.
    }

    /// <inheritdoc />
    protected CustomException(SerializationInfo info,
      StreamingContext context) : base(info, context)
    {
        // Inherit documentation from the base Exception class matching
        // this constructor's signature.
    }
}
Interface Implementation Examples
/// <summary>
/// A class with an explicit interface implementation
/// </summary>
/// <remarks>Note that you must enable the <b>DocumentExplicitInterfaceImplementations</b>
/// SHFB project options in order to see the explicitly implemented members.</remarks>
/// <conceptualLink target="86453FFB-B978-4A2A-9EB5-70E118CA8073" />
public class ExplicitImplementation : ICollection, ICloneable, IEnumerable
{
    #region ICollection Members

    void ICollection.CopyTo(Array array, int index)
    {
        // Comments are automatically inherited for explicit
        // interface members with no comments.
    }

    int ICollection.Count
    {
        get
        {
            // Comments are automatically inherited for explicit
            // interface members with no comments.
            return 0;
        }
    }

    bool ICollection.IsSynchronized
    {
        get
        {
            // Comments are automatically inherited for explicit
            // interface members with no comments.
            return true;

        }
    }

    /// <inheritdoc />
    /// <remarks>This is a dummy class and always returns null.</remarks>
    object ICollection.SyncRoot
    {
        get
        {
            // In this case, we inherit the <summary> and <returns>
            // comments and add a <remarks> comment.  Because we added
            // comments, we need to specify the <inheritdoc /> tag too.
            return null;
        }
    }

    #endregion

    #region IEnumerable Members

    /// <inheritdoc />
    /// <returns>This is a dummy class so it throws an exception</returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        // In this case, we automatically inherit the base interface's
        // <summary> but override the <returns> documentation.  As above,
        // because we specified comments, we have to add the <inheritdoc />
        // tag too.
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    #region ICloneable Members

    /// <inheritdoc />
    public object Clone()
    {
        // Not explicitly implemented so we have to tell it to inherit
        // documentation on this one.
        return null;
    }

    #endregion
}
Various Other Examples
#region Base class
//=========================================================================

/// <summary>
/// A base class from which to inherit documentation
/// </summary>
/// <remarks>
/// <para>These remarks are for the base class.</para>
///
/// <para>This information applies to all classes that derive from
/// <see cref="BaseInheritDoc"/>:
/// <list type="bullet">
/// <item><description>Point #1.</description></item>
/// <item><description>Point #2.</description></item>
/// <item><description>Point #3.</description></item>
/// </list>
/// </para>
/// </remarks>
/// <conceptualLink target="86453FFB-B978-4A2A-9EB5-70E118CA8073" />
public class BaseInheritDoc
{
    /// <summary>
    /// Constructor
    /// </summary>
    public BaseInheritDoc()
    {
    }

    /// <summary>
    /// The ToString implementation for BaseInheritDoc
    /// </summary>
    /// <returns>A string representing the object</returns>
    public override string ToString()
    {
        return base.ToString();
    }

    /// <summary>
    /// Summary for the method with an example
    /// </summary>
    /// <returns>True all the time</returns>
    /// <example>
    /// This example is from the base class
    /// <code>
    /// // 'x' is always true
    /// bool x = instance.MethodWithExample();
    /// </code>
    /// </example>
    public virtual bool MethodWithExample()
    {
        return true;
    }

    /// <summary>
    /// The method in the base class has lots of comments.
    /// </summary>
    /// <remarks>Remarks for the base class</remarks>
    /// <param name="x">The parameter</param>
    /// <exception cref="ArgumentException">Thrown if x is zero</exception>
    /// <exception cref="ArgumentOutOfRangeException">Thrown if x is
    /// less than zero.</exception>
    /// <example>
    /// <code>
    /// /// Example goes here
    /// </code>
    /// </example>
    /// <seealso cref="ToString" />
    /// <seealso cref="MethodWithExample"/>
    public virtual void MethodWithLotsOfComments(int x)
    {
    }

    /// <summary>
    /// A method with two examples
    /// </summary>
    /// <example>
    /// <span id="Example 1">
    /// This is example #1:
    /// <code>
    /// // Example #1
    /// </code>
    /// </span>
    /// <span id="Example 2">
    /// This is example #2:
    /// <code>
    /// // Example #2
    /// </code>
    /// </span>
    /// </example>
    protected virtual void MethodWithTwoExamples()
    {
        // By using a <span> with an ID, we can group comments for
        // selection by an override in a derived class.
    }
}
#endregion

#region Derived class
//=========================================================================

/// <summary>
/// This is a derived class with inherited documentation.
/// </summary>
/// <remarks>This will inherit just the last &lt;para&gt; tag from
/// the base class's &lt;remarks&gt; tag:
/// <inheritdoc path="para[last()]" />
/// </remarks>
/// <conceptualLink target="86453FFB-B978-4A2A-9EB5-70E118CA8073" />
public class DerivedClassWithInheritedDocs : BaseInheritDoc
{
    // Note in the <remarks> tag above that we can inherit specific
    // parts of a comment tag's text by using an XPath query.  This
    // can allow you to merge comments from various sources into one
    // set of comments in a given tag.  An implied filter that limits
    // the selection to the <remarks> tag is added automatically.  If
    // the path attribute were omitted, the entire set of remarks
    // from the base class would be inherited.

    /// <inheritdoc cref="Object.ToString" />
    public override string ToString()
    {
        // This override ignores the base class comments and uses a
        // cref attribute to obtain the comments from
        // System.Object.ToString instead.
        return base.ToString();
    }

    /// <summary>
    /// This overloaded method does something
    /// </summary>
    /// <param name="p1">The string parameter</param>
    /// <overloads>
    /// <summary>There are three overloads for this method.</summary>
    /// <remarks>These remarks are from the overloads tag on the
    /// first version.</remarks>
    /// </overloads>
    public void OverloadedMethod(string p1)
    {
    }

    #pragma warning disable 1573
    /// <inheritdoc cref="OverloadedMethod(string)" />
    /// <param name="p2">The second string parameter</param>
    public void OverloadedMethod(string p1, string p2)
    {
        // Inherit documentation from the first overload and add
        // comments for the second parameter.

        // Note that because we supplied comments for one parameter
        // but not the other, the compiler will complain.  However,
        // we can shut it up by using a "#pragma warning" directive as
        // shown.
    }

    /// <inheritdoc cref="OverloadedMethod(string)" path="param|overloads/*" />
    /// <param name="x">An integer parameter</param>
    public void OverloadedMethod(string p1, int x)
    {
        // This example inherits the comments from the <param> tag on
        // the first version, the content of the <overloads> tag on the
        // first version, and adds comments for the second parameter.
    }
    #pragma warning restore 1573

    /// <summary>
    /// An override of the method with an example
    /// </summary>
    /// <returns>Always returns false</returns>
    /// <example>
    /// <inheritdoc />
    /// <p/>This example applies to the derived class:
    /// <code>
    /// if(derivedInstance.MethodWithExample())
    ///     Console.WriteLine("This is never reached");
    /// </code>
    /// </example>
    public override bool MethodWithExample()
    {
        // The <example> tag inherits the example from the base class
        // and adds a new example of its own.  Again, an implied filter
        // limits the nested tag to inheriting comments from the
        // <example> tag in the base class's comments.

        return false;
    }

    /// <inheritdoc path="summary|remarks|param" />
    public override void MethodWithLotsOfComments(int x)
    {
        // For this override, we don't want all the comments, just those
        // from the <summary>, <remarks>, and <param> tags.
    }

    /// <summary>
    /// This only includes one of the examples
    /// </summary>
    /// <example>
    /// <inheritdoc path="span[@id='Example 2']" />
    /// </example>
    protected override void MethodWithTwoExamples()
    {
        // Here, we use a filter to select a group of comments in
        // a <span> tag from the base member's <example> tag.
    }

    /// <summary>
    /// This uses a shared example from a base member that is not
    /// public and this doesn't override.
    /// </summary>
    /// <example>
    /// <inheritdoc cref="MethodWithTwoExamples" path="span[@id='Example 2']" />
    /// </example>
    public void MethodUsingSharedExample()
    {
        // This method uses a cref attribute and a path attribute to inherit
        // a specific example from a member to which it has no relation.
    }
}
#endregion

See Also