">
Using the <inheritdoc /> Tag |
The use of the <inheritdoc /> custom XML comments tag can help minimize the effort required to document complex APIs by allowing common documentation to be inherited from base types/members.
The use of the tag 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 tags allows you to inherit common information such as value and parameter descriptions while overriding the inherited documentation for other tags 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 tag is as follows:
<inheritdoc [cref="link-ref"] [select="filter-expr"] />
This optional attribute overrides the standard search method to allow documentation inheritance from an alternate user-specified member indicated by the link-ref value.
This optional attribute applies a specified XPath filter expression to the inherited comments. This is useful if you want to limit the inherited documentation to a specific subset of tags 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 select attributes either by themselves or together, you can fine tune the inheritance of documentation. You can also nest the tag within other tags to further refine the level of inheritance.
The <inheritdoc /> tag is valid at the root level (i.e. the same level as <summary> tags) 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 tag 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 /> tag from them unless you want to customize the comments.
With or without an explicit cref attribute, if the inherited documentation itself contains <inheritdoc /> tags, they will be expanded recursively working backwards up the inheritance chain.
In all cases, if a select 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 tags already exist in the member's comments, the inherited versions are ignored:
<example>
<exclude>
<filterpriority>
<preliminary>
<summary>
<remarks>
<returns>
<threadsafety>
<value>
The <overloads> tag will never be inherited. This prevents the doubling of comments on the overloads page. However, you can inherit the contents of the <overloads> tag using a select attribute with a value of "overloads/*". See below for an example.
All other tags will be inherited unless they match a tag 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 tags from one or more sources, use one or more nested <inheritdoc /> tags within the given tag. See below for examples.
Be aware that when <param> tags 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.
The <inheritdoc /> tag can also be nested within other XML comments tags such as <summary>, <remarks>, <example> etc. in order to inherit specific parts of the documentation within those tags. 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 tag or tags within which the <inheritdoc /> tag is nested. The cref and select attributes can also be applied to further qualify how the documentation is inherited. If you do not want to have the parent tags automatically included in the filter, you must supply a select attribute with a rooted XPath query that specifies from where to obtain the comments (i.e. select="/summary/node()").
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 documentation assemblies in your project.
Since the XML comments produced by the compiler are incomplete, it is recommended that you make use of the IntelliSense 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.
The following show various examples of using the <inheritdoc /> tag. See the comments within each for details about what the examples are showing. The Sandcastle XML Comments Guide installed as part of the Sandcastle tools contains working examples that you look at to see the end result.
/// <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. } }
/// <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 }
-#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 <para> tag from /// the base class's <remarks> tag: /// <inheritdoc select="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 select 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)" /// select="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 select="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 select="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" /// select="span[@id='Example 2']" /> /// </example> public void MethodUsingSharedExample() { // This method uses a cref attribute and a select tag to inherit // a specific example from a member to which it has no relation. } } #endregion