Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetDocumentation() not working on members of generic classes #52

Closed
bert2 opened this issue May 1, 2020 · 7 comments
Closed

GetDocumentation() not working on members of generic classes #52

bert2 opened this issue May 1, 2020 · 7 comments
Assignees
Labels
Bug issues that suggest a flaw with existing code

Comments

@bert2
Copy link

bert2 commented May 1, 2020

GetDocumentation() does not work (i.e. returns null) when members reference generic type arguments of the enclosing type.

Repro steps

  1. Create a generic type e.g:
public class MyGenericClass<T> {
    /// <summary>foo method</summary>
    public void Foo(T x) {}
}
  1. Call GetDocumentation() on it:
Assert.IsNotNull(typeof(MyGenericClass<T>).GetMethod("Foo").GetDocumentation());

Expected

GetDocumentation() should return something like "<summary>foo method</summary>" (plus some whitespace around it).

Actual

GetDocumentation() returns null.

Additional context

The root cause of this is that Type.IsGenericParameter is only true for parameters using type arguments of the method, but it is false for parameters using type arguments of the class.

You can verify that here: https://dotnetfiddle.net/belm44

Related question I just posted on StackOverflow: https://stackoverflow.com/questions/61542867/how-to-get-type-genericparameterposition-for-method-parameter-of-class-generic

@bert2 bert2 added the Bug issues that suggest a flaw with existing code label May 1, 2020
@ZacharyPatten ZacharyPatten self-assigned this May 1, 2020
@ZacharyPatten ZacharyPatten added this to the First Release milestone May 1, 2020
@ZacharyPatten
Copy link
Owner

ZacharyPatten commented May 1, 2020

@bert2 Thanks for the input. I was able to reproduce this issue.

Workaround

There is a workaround you can use at the moment. Rather than supplying a placeholder type in the generics, you can just use empty generics:

Assert.IsNotNull(typeof(MyGenericClass<>).GetMethod("Foo").GetDocumentation());

I removed the T from MyGenericClass<T>. This should work with the current code in Towel. This works with multiple generic parameters too; you just add commas: MyGenericClass<,,,>.

Fix

There is probably a fix so that you do not need to remove the explicit generic types before calling GetDocumentation in Towel. I will look into a fix for that. :)

@bert2
Copy link
Author

bert2 commented May 1, 2020

The accepted answer on my StackOverflow question mentions using GetGenericTypeDefinition() on a constructed generic type to turn it into an open generic type again. This should do the trick.

@ZacharyPatten
Copy link
Owner

ZacharyPatten commented May 1, 2020

So GetGenericTypeDefinition() is nice, but it works on Type rather than MethodInfo. I'm given a MethodInfo in that overload of GetDocumentation(). I could grab the DeclaringType off the MethodInfo and then call GetGenericTypeDefinition() on the DeclaringType, but then I would have no way to get back to the respective MethodInfo as there could be overloads of that method (I cannot simply look it up by name).

For example, there could be something like this:

class A<T>
{
 void B(int a) {}
 void B(T a) {}
}

Those are two seperate methods, but if we are dealing with type A<int> then they have the same signature.

There is a GetGenericMethodDefinition() but that doesn't appear to do exactly what is needed either.

What is actually needed is a way to tell if a ParameterInfo on a MethodInfo.GetParameters() was defined as a generic type, and if it was, which generic from the DeclaringType is it. I don't think there are any exposed methods for doing this currently without getting dirty and looking into the IL manually (if even possible).

@ZacharyPatten
Copy link
Owner

ZacharyPatten commented May 1, 2020

I am curious though. Did you run into a use case where you cannot use the generic definition (so that the workaround I mentioned is not possible)?

  • generic: typeof(MyClass<>).GetMethod("MyMethod")
  • non-generic (using int): typeof(MyClass<int>).GetMethod("MyMethod")

Cause what I should probably do is just force people to provide the GetDocumentation() method with the generic types, and throw a ArgumentException if they are not the generic definitions.

@bert2
Copy link
Author

bert2 commented May 2, 2020

Hi @ZacharyPatten, thanks for investigating this.

I'm currently trying to fix a related bug in a tool to auto-generate markdown from an assembly having XML documentation. Through google I found your article/library and just wanted to inform you about this issue.

In my case I might get away by using GetGenericTypeDefinition() on all generic types the tool finds in the specified assembly before descending into the method/parameter analysis.

Your case, however, is more complicated as you explained. To me it seems that clients of your library have to be aware of this and use either the open generic type or GetGenericTypeDefinition() when they hit that problem.

@ZacharyPatten
Copy link
Owner

I'm so dumb. I found a fix... and it was really easy... there is a MetadataToken. If the DeclaringType is generic via IsGenericType, then I just need to call GetGenericTypeDefinition then GetMethods and find the MethodInfo with the matching MetadataToken. Then I will have the generic MethodInfo if it was indeed generic.

I will have this fixed in the next release.

Sorry this took so long. @bert2

@bert2
Copy link
Author

bert2 commented Jul 7, 2021

No worries, @ZacharyPatten!

...and now I finally know what the MetadataToken is actually for 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug issues that suggest a flaw with existing code
Projects
None yet
Development

No branches or pull requests

2 participants