I’ve heard several different opinions about how the debugger should display a generic method in the callstack.
Say you have a method Class<T>::Method<S>(…). Say you have 3 instances, of with (T=int, S=string), (T=int, S=object), and (T=float, S=object). What’s more natural display?
2.) Class<int>::Method<object> (T=int, S=string, …)
Class<int>::Method<object> (T=int, S=object, …)
Class<float>::Method<object> (T=float, S=object, …)
3.) something else?
I think it comes down to what you’re looking for. #1 is more natural from a high-level end-user. However, #2 is appears more natural to the low-level user. Some specific things to consider:
Generics & Code-sharing:
The CLR does code-sharing amongst generic types when applicable. ‘string’ and ‘object’ can share, but ‘int’ and ‘float’ can not.
Method<String> and Method<object> may reuse the same code. It then passes a hidden parameter so that Method<T> can evaluate typeof(T) in its body. #2 exposes this in a natural way because it maintains the 1:1 mapping between function name and native code. This mapping can be very signficant for hard core debugging at an assembly level. Notice the shared instances have the same function name “Class<int>::Method<object>”. In contrast, #1 hides the code sharing because it provides two different function names for the same code.
Generics & the hidden parameter
#2 also exposes generic’s hidden parameter, by actually decoding it into the generic parameters and placing it in the parameter list. This can become even more signficant in optimized builds when the generic parameter has been optimized away!
Parity with language usage.
In contrast, if you want to focus on language usage and consider code-sharing and hidden parameters an implementation detail and don’t want to think about it, then #1 will be more intuitive.
Scaling to nested generic types:
What if S=List<int>? #1 scales to this solution more easily and would become Class<int>::Method<List<int>>. It’s not clear what the correct response for style #2 even is in this case.