How to reference generic classes and methods in xml documentation
Asked Answered
S

8

253

When writing xml documentation you can use <see cref="something">something</see>, which works of course. But how do you reference a class or a method with generic types?

public class FancyClass<T>
{
  public string FancyMethod<K>(T value) { return "something fancy"; }
}

If I was going to write xml documentation somewhere, how would I reference the fancy class? how can I reference a FancyClass<string>? What about the method?

For example in a different class I wanted to let the user know that I will return an instance of FancyClass<int>. How could I make a see cref thing for that?

Scrivens answered 10/2, 2009 at 12:50 Comment(0)
W
320

To reference the method:

/// <see cref="FancyClass{T}.FancyMethod{K}(T)"/> for more information.
Women answered 10/2, 2009 at 13:1 Comment(4)
Thanks for that answer! It's actually missing from MSDN's page on <see>: msdn.microsoft.com/en-us/library/acd0tfbe.aspxSingularity
I actually believe for it to also work in VS2010 tooltips you need to indicate the number of generic arguments, e.g. "FancyClass1{T}.FancyMethod1{K}(T)"Ar
Not sure what you mean about that. I have never had to add those, and it has always worked for me. Do you have a specific example where it doesn't work? If so, please post it somewhere (or even provide an answer yourself.)Women
@Lasse, please see Steve's answer and comments below. Your answer doesn't cover correct Intellisense tooltips.Valina
Q
78

TL;DR:

"How would I reference FancyClass<T>?"

   /// <see cref="FancyClass{T}"/>

"What about FancyClass<T>.FancyMethod<K>(T value)?"

   /// <see cref="FancyClass{T}.FancyMethod{K}(T)"/>

"How can I reference a FancyClass<string>?"

   /// <see cref="SomeType.SomeMethod(FancyClass{string})"/>
   /// <see cref="FancyClass{T}"/> whose generic type argument is <see cref="string"/>

While you can reference a method whose signature includes FancyClass<string> (e.g. as a parameter type), you cannot reference such a closed generic type directly. The second example works around that limitation. (This is seen e.g. on the MSDN refence page for the static System.String.Concat(IEnumerable<string>) method). :

XML documentation comment cref rules:

  • Surround the generic type parameter list with curly braces {} instead of with <> angle brackets. This spares you from escaping the latter as &lt; and &gt; — remember, documentation comments are XML!

  • If you include a prefix (such as T: for types, M: for methods, P: for properties, F: for fields), the compiler will not perform any validation of the reference, but simply copy the cref attribute value straight to the documentation XML output. For this reason, you'll have to use the special "ID string" syntax that applies in such files: always use fully-qualified identifiers, and use backticks to reference generic type parameters (`n on types, ``n on methods).

  • If you omit the prefix, regular language naming rules apply: you can drop namespaces for which there's a using statement, and you can use the language's type keywords such as int instead of System.Int32. Also, the compiler will check the reference for correctness.

XML documentation comment cref cheat sheet:

namespace X
{
    using System;

    /// <see cref="I1"/>  (or <see cref="X.I1"/> from outside X)
    /// <see cref="T:X.I1"/>
    interface I1
    {
        /// <see cref="I1.M1(int)"/>  (or <see cref="M1(int)"/> from inside I1)
        /// <see cref="M:X.I1.M1(System.Int32)"/>
        void M1(int p);

        /// <see cref="I1.M2{U}(U)"/>
        /// <see cref="M:X.I1.M2``1(``0)"/>
        void M2<U>(U p);

        /// <see cref="I1.M3(Action{string})"/>
        /// <see cref="M:X.I1.M3(System.Action{System.String})"/>
        void M3(Action<string> p);
    }

    /// <see cref="I2{T}"/>
    /// <see cref="T:X.I2`1"/>
    interface I2<T>
    {
        /// <see cref="I2{T}.M1(int)"/>
        /// <see cref="M:X.I2`1.M1(System.Int32)"/>
        void M1(int p);

        /// <see cref="I2{T}.M2(T)"/>
        /// <see cref="M:X.I2`1.M2(`0)"/>
        void M2(T p);

        /// <see cref="I2{T}.M3{U}(U)"/>
        /// <see cref="M:X.I2`1.M3``1(``0)"/>
        void M3<U>(U p);
    }
}
Quoits answered 18/12, 2016 at 12:7 Comment(2)
How to refer just the T part?Almondeyed
Figured out: <typeparamref name="T"/>Almondeyed
B
50
/// <summary>Uses a <see cref="FancyClass{T}" /> instance.</summary>

BTW, it was present in the MSDN documentation of .Net Framework 2.0 and 3.0, but it disapeared in the version 3.5

Build answered 10/2, 2009 at 12:53 Comment(5)
what about a spesific instance of T? like string? Maybe not possible?Scrivens
what do you mean? You can't declare a specific version, so you can't refer to it either.Women
If a method for example only returns List<string> for example. But not important :)Scrivens
Yes I was wondering also... resharpers squiggles when writing FancyClass{string} but not when writing FancyClass{String}...Build
The reason for the above observation by "Think Before Coding" is that it doesn't work with the c# aliases. For example you need to use Int32 instead of int, Single instead of float etc. (Putting this info here in case anyone else stumbles on this)Gyrostatics
V
26

None of the answers shown so far work completely for me. ReSharper won't convert the see tag into a Ctrl+click-able link (e.g. image here) unless it completely resolves.

If the method in the OP were in a namespace called Test, the completely resolved link to the method shown would be:

<see cref="M:Test.FancyClass`1.FancyMethod``1(`0)"/>

As you may be able to work out, there should only be one backtick before the number of class type parameters, then two backticks before the number of method type parameters, then the parameters are the zero-indexed parameter with the appropriate number of backticks.

So we can see that FancyClass has one class type parameter, FancyMethod has one type parameter, and an object of the FancyClass parameter type will be passed to the method.

As you can more clearly see in this example:

namespace Test
{
    public class FancyClass<A, B>
    {
        public void FancyMethod<C, D, E>(A a, B b, C c, D d, E e) { }
    }
}

The link becomes:

M:Test.FancyClass`2.FancyMethod``3(`0,`1,``0,``1,``2)

Or "Class with two type parameters which has a method with three type parameters where the method parameters are ClassType1, ClassType2, MethodType1, MethodType2, MethodType3"


As an additional note, I didn't find this documented anywhere and I'm not a genius, the compiler told me all this. All you have to do is create a test project, enable XML documentation, then insert the code you want to work out a link for, and put the start of an XML doc comment on it (///):

namespace Test
{
    public class FancyClass<T>
    {
        ///
        public string FancyMethod<K>(T value) { return "something fancy"; }
    }

    public class Test
    {
        public static void Main(string[] args) { }
    }
}

Then build your project, and the outputted XML documentation includes the link in the doc->members->member element under the attribute name:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Test</name>
    </assembly>
    <members>
        <member name="M:Test.FancyClass`1.FancyMethod``1(`0)">

        </member>
    </members>
</doc>
Vibrate answered 10/12, 2015 at 17:37 Comment(1)
This should get more upvotes, especially because of the trick to find the correct notation, without having to go through trial-and-error. Kudos my manChuvash
A
10

Further from the answers by Lasse and T.B.C:

/// <see cref="T:FancyClass`1{T}"/> for more information.

/// <see cref="M:FancyClass`1{T}.FancyMethod`1{K}(T)"/> for more information.

will also provide tooltips correctly, whereas their version renders it with the curly braces.

Ar answered 26/10, 2011 at 11:48 Comment(10)
Using <see cref="System.Collections.Generic.List1{T}"/>** causes a build-time warning: **XML comment on 'Blah' has syntactically incorrect cref attribute 'System.Collections.Generic.List1<T> - would you care to elaborate on how one should use this?Valina
Hi Jakub, This does indeed not seem to work. The only way I can also get tooltips to work correctly is by <see cref="T:<fullTypeName>`1{T}" />.Ar
thanks, all works correctly now. Both T: and the explicit (and correct!) number of generic parameters are needed to compile without warnings and to have correct Intellisense tooltips. Please update your answer, I will be happy to upvote it, as it is the most complete one of all. BTW, I have yet to see how this looks after the documentation is compiled into HTML/whatever.Valina
BTW, how do we use it on generic methods in generic classes? None of the above works (Intellisense is not correct): <see cref="M:System.Collections.Generic.List1{T}.Add1(T)"/>, <see cref="M:System.Collections.Generic.List1{T}.Add1{T}(T)"/>, <see cref="M:System.Collections.Generic.List1{T}.Add1(K)"/>, <see cref="M:System.Collections.Generic.List1{T}.Add1{K}(K)"/>.Valina
OK, I partially got it. If the method itself is not generic (like in List<T>.Add()), this works: <see cref="M:System.Collections.Generic.List`1{T}.Add(T)"/>.Valina
But if I have a generic class with a generic method (with different generic parameters), it gets tough. E.g. I have: namespace Foo { public class Bar<T, K> { public void Baz(T t) { } public void Qux(T t) { } public void Qux<M>(M m) { } } } Both <see cref="M:Foo.Bar2{T, K}.Qux(T)"/> and <see cref="M:Foo.Bar2{T, K}.Qux(M)"/> seems to display correctly, but I would expect to be able to use <see cref="M:Foo.Bar2{T, K}.Qux1{M}(M)"/> to make in unambiguous. This unfortunately doesn't work.Valina
Doesn't seem to be working for me. I have <see cref="M:System.Collections.Generic.List`1{T}"/> in the comment header for a generic extension method I wrote (converts an ArrayList to a List<T>) but ReSharper flags it as being a syntax error, and IntelliSense just displays it verbatim. VS 2010/R# 6.1.37.86Polito
Aha! I was able to get <see cref="T:System.Collections.Generic.List`1"/>" to work. So, using T: instead of the curly braces did the trick. It does expand out the full namespace, and the trick does not work if you do not include the namespace, so it's not perfect, but it will do.Polito
@StephenDrew: I recommend you integrate the conclusions arrived at from the comments into your answer. Not many people read all the comments and so some may conclude that your answer doesn't work.Amanuensis
Tried my best but I only use VS2015 now and cannot verify the correctness :( Thankfully it is much easier to do this now in VS2015.Ar
A
7
/// Here we discuss the use of <typeparamref name="TYourExcellentType"/>.
/// <typeparam name="TYourExcellentType">Your exellent documentation</typeparam>
Assure answered 10/11, 2016 at 15:54 Comment(1)
Note that the other answers cover how to reference a generic class, this answer shows you how to reference the type parameter on its own, which happened to be what I was looking to do.Gaeta
K
1
/// <see cref="FancyClass&lt;T>.FancyMethod&lt;K>(T)"/> for more information.
Kearse answered 28/1, 2012 at 15:25 Comment(0)
P
1

Here's an answer I gave somewhere else. It will work for classes and methods too.

I tried everything on stack overflow to a get results that work under several scenarios. Here's a solution that works for me. (That's subjective concerning anyone else.)

  1. Produces clickable links.
  2. Hovering over identifiers works.
  3. Produces .xml file correctly.
  4. Produces no errors in intellisense.
  5. Works for nullable generic type parameters.
  6. Works in Resharper and it's built-in XML Doc window (Resharper -> Edit -> Show Quick Documentation)
  7. Works in XAM Doc Preview for Atomineer Pro Documentaion Visual Studio Extension.
  8. Works with a generic type of a generic type.

Example #1

  /// <summary>
  ///  This instance field holds a reference to the
  ///  <see cref="ConcurrentDictionary{Decimal, Boolean}"/> as
  ///  <see cref="T:ConcurrentDictionary&lt;decimal, bool?&gt;"/> that contains
  ///  the list of all PDF's that are currently opened and being displayed.
  /// </summary>
  private ConcurrentDictionary<decimal, bool?> openedPdfs = default!;

  Note: 
    The ConcurrentDictionary{Decimal, Boolean} will correctly produce a
    clickable link of ConcurrentDictionary{TKey, TValue} on hovering while
    T:ConcurrentDictionary&lt;decimal, bool?&gt; makes sure the reader gets
    information on what type TKey and TValue are.

Example # 2 (using "T")

  /// <summary>
  ///  This instance field holds a reference to the
  ///  <see cref="ConcurrentDictionary{TKey, TValue}"/> as
  ///  <see cref="T:ConcurrentDictionary&lt;decimal, bool?&gt;"/> that contains
  ///  the list of all PDF's that are currently opened and being displayed.
  /// </summary>
  private ConcurrentDictionary<decimal, bool?> openedPdfs = default!;
Pernod answered 12/3, 2021 at 22:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.