When I'm writing functions for my project, and more specifically, their XML documentation comments, I find myself repeating the comments for a specific parameter often. This leads to misleading documentation sometimes (as copy pasting usually does...).
This is a simple example I thought of, which represents the real problem.
/// <summary>
/// The number that should be doubled
/// </summary>
private static float myNumber = 10f;
/// <summary>
/// Multiplies a number by 2
/// </summary>
/// <param name="number"><inheritdoc cref="myNumber"/></param>
/// <returns>The number multiplied by 2</returns>
private static float MultiplyByTwo(float number)
{
return number * 2f;
}
In this line /// <param name="number"><inheritdoc cref="myNumber"/></param>
, I would like to have the text "The number that should be doubled", but it's not showing up. Maybe I don't understand the use of inheritdoc completely.
What I mean by showing up is this. Visual Studio should show the documentation of number
in that box:
This is what it should look like (without copy pasting the text):
So, is there a way to reference a different variable in XML documentation comments?
In Visual Studio 16.8.4 I'm able to use <inheritdoc>
's path
attribute to do this.
/// <summary>
/// The number that should be doubled
/// </summary>
private static float myNumber = 10f;
/// <summary>
/// Multiplies a number by 2
/// </summary>
/// <param name="number"><inheritdoc cref="myNumber" path="/summary"/></param>
/// <returns></returns>
private static float MultiplyByTwo(float number)
{
return number * 2f;
}
In the path
attribute, /
selects the 'root' node and then summary
is the node to select within that node.
Result:
The path
attribute uses XPath syntax, which you can find more about here.
You can use it to do some great stuff if you're careful; I regularly use it when implementing the Try[...]
pattern.
For example:
/// <summary>
/// This throws!
/// </summary>
/// <param name="param1">This is a parameter.</param>
/// <param name="param2">This is another parameter!</param>
/// <exception cref="InvalidOperationException"/>
public string ExampleThatCanThrow(int param1, float param2)
{
throw new InvalidOperationException();
}
/// <summary>
/// This never throws!
/// </summary>
/// <inheritdoc cref="ExampleThatCanThrow(int, float)" path="/*[not(self::exception)]"/>
public bool TryExample(int param1, float param2, out string? result)
{
result = "No throwing here!";
return true;
}
Normally when using <inheritdoc>
for the TryExample
method in this way, it would show that it could throw InvalidOperationException
. Using the path
attribute, I've filtered it so only the nodes that don't match the name of exception
will be inherited.
/
: Matches the root node.
*
: Matches any child node.
[
and ]
: Matches any node that meets the conditions of the contained predicate.
not()
: Matches any node that doesn't meet the conditions of the expression within the parentheses.
self::exception
: Matches the current node if it has the name of exception
.
This results in the below:
In addition, you can use this functionality to more easily show the exceptions that can be thrown by a method without typing them out explicitly:
/// <summary>
/// Validates a file in some way.
/// </summary>
/// <param name="filePath">A full path to the file to be validated.</param>
/// <inheritdoc cref="File.OpenRead(string)" path="/exception"/>
private static void ValidateFile(string filePath)
{
using FileStream file = File.OpenRead(filePath);
// Validation code...
}
This above use of <inheritdoc>
will cause the tooltip on the method to show that it can throw all of the exceptions that the System.IO.File.OpenRead
method can throw. Just be careful that you ensure necessary validation exceptions are actually thrown and accounted for.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With