dotnet / standard

This repo is building the .NET Standard
3.06k stars 428 forks source link

System.Reflection not finding custom attributes on Generic Parameters #837

Closed mwpowellhtx closed 6 years ago

mwpowellhtx commented 6 years ago

Seems similar to this Mono issue, except for Class Generics, not Method Generics.

So any custom attribute, something like:

[AttributeUsage(AttributeTargets.GenericParameter, AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public MyCustomAttribute(...) {}
}

Literally, I've got a class like this:

public class MyCustomClass<[MyCustom(...)] T> {}

I am finding lots of ways to get at the list of Generic Types, per se, but not the list of Generic Parameters, if that makes any sense. In other words, any requests for Custom Attributes appear to be on the typeof(T), but not a sort of the GenericParameterInfo if there were such a thing.

I am targeting .NET Standard 2.0 for a bit of Development Only dependency assembly work.

jnm2 commented 6 years ago

@mwpowellhtx Sorry, I answered in the chat but didn't see this issue.

When typeof(T) executes, it will always be replaced with the parameterized type of the class's generic instantiation. typeof(T) won't be the generic type parameter; it'll be identical to if you had written typeof(int) or typeof(Foo) there. If you want the definition itself of the generic type parameter, you need to use GetGenericArguments(), like this:

typeof(MyClass<>).GetGenericArguments()[0].CustomAttributes
mwpowellhtx commented 6 years ago

@jnm2 Yes, I know about typeof, that's not the point. Given a generic type, how do I access the GenericParameterInfo (?), in order to get the custom attributes of that? The issue is, as I mentioned here, GetGenericArguments returns a Type[], not GenericParameterInfo[]. If you try GetCustomAttributes after that, you are getting custom attributes on the Type T, not on its decoration in the Generic list. Does this make sense? Cook up a simple unit test to demonstrate it for yourself if you don't believe me.

mwpowellhtx commented 6 years ago

@jnm2 A similar case was observed in the Mono project for Method generics, as compared/contrasted with Class generics.

jnm2 commented 6 years ago

@mwpowellhtx I had run the code and typeof(MyClass<>).GetGenericArguments()[0].CustomAttributes returned the MyCustomAttribute. I'll try again and try to see what I'm missing.

The Type class represents all the generic parameter info there is, for types that are generic parameters.

terrajobst commented 6 years ago

@mwpowellhtx

This code:

interface Type
{
        void Method<[Obsolete] T>();
}

Doesn't compile with the Roslyn C# compiler:

CS0592: Attribute 'Obsolete' is not valid on this declaration type. It is only valid on 'class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations.

/cc @jaredpar

While valid in the underlying the IL, I don't think anyone on our end has ever relied on discovering attributes on generic type instantiations. BTW, in IL you can attribute virtually anything, including derived types, which C# also doesn't let you do. The only thing you can't attribute in IL is attribute application. /cc @tmat

In general, I don't believe this issue belongs here as your asking for implementation/API surface changes of a net-new feature. This issue belongs to dotnet/corefx.

mwpowellhtx commented 6 years ago

@terrajobst Why is this closed? I'm not talking about Method generics, although there may be some similarities there, I do not know; but rather, Class generics?

Who's not relying on it? I am reported it. At least some others have reported this. So obviously, SOME of us are relying on this, or would like to if it were possible.

mwpowellhtx commented 6 years ago

@terrajobst To be clear, I'm not talking about ObsoleteAttribute in particular, but rather a custom attribute in my case.

mwpowellhtx commented 6 years ago

@terrajobst Also, I am targeting .NET Standard, not .NET Core, just to be clear about that, so kindly reopen for investigation. Thanks!

jnm2 commented 6 years ago

@mwpowellhtx Can you come over to https://gitter.im/dotnet/csharplang to follow up on your question and my reply there? I think there's a misunderstanding.

mwpowellhtx commented 6 years ago

@jnm2 Here is what I get when I pursue the suggested avenue:


  | Name | Value | Type
-- | -- | -- | --
▶ | typeof(AssemblyInfoBumpVersionService<T>).GetGenericArguments()[0] | {Name = "AssemblyFileVersionAttribute" FullName = "System.Reflection.AssemblyFileVersionAttribute"} | System.Type {System.RuntimeType}
◢ | a | {System.Attribute[3]} | System.Attribute[]
  | ▶ [0] | {System.AttributeUsageAttribute} | System.Attribute {System.AttributeUsageAttribute}
  | ▶ [1] | {System.Runtime.InteropServices.ComVisibleAttribute} | System.Attribute {System.Runtime.InteropServices.ComVisibleAttribute}
  | ▶ [2] | {__DynamicallyInvokableAttribute} | System.Attribute {__DynamicallyInvokableAttribute}

I find the generic parameter to be AssemblyFileVersionAttribute correctly so. However, I find custom attributes for the AssemblyFileVersionAttribute type, not for its parameter decoration.

mwpowellhtx commented 6 years ago

@terrajobst Turns out the confusion was mine over a difference between typeof(MyClass<T>) and typeof(MyClass<>). Subtle difference and reveals different generic type reflection.

terrajobst commented 6 years ago

@mwpowellhtx

I closed this issue because I don't think it belongs into this repo but maybe we're talking past each other. Let me rephrase:

  1. My understanding is that you're using attributes on a generic type instantiation in Mono. It looks like Mono's C# compiler supports that.
  2. This isn't supported by the Roslyn/C# compiler. So this is either a compiler limitation, in which case it belongs to dotnet/roslyn or a C# language limitation, in which case it belongs to dotnet/csharplang.
  3. You're unable to find the custom attribute you applied using Mono's compiler. That's not entirely surprising given that the Microsoft compiler never supported this and thus the vast majority of developers never had to ask the question. As I said, they are valid in IL though so I think it's reasonable to ask them to be exposed from reflection. This is either a behavior change of an existing API or a new API. Either way, the request would go to dotnet/corefx.

This repo only tracks API suggestions for .NET Standard. One requirement is that these API already exist in at least one .NET implementations. As such, I closed this issue.

mwpowellhtx commented 6 years ago

@terrajobst To clarify, no, my Mono reference is only in terms of reflecting generic type attribution. That's the only parallel there.

Rather, I'm interested to reflect attributes decorating T in class MyClass<T> {}.

But this has been resolved.

terrajobst commented 6 years ago

Ah, I stand corrected. I should have used your repro code instead of the one linked from Mono issue:

[AttributeUsage(AttributeTargets.GenericParameter, AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public MyCustomAttribute() { }
}

public class MyCustomClass<[MyCustom] T>
{
}

This indeed compiles just fine. And yes, typeof(MyClass<>) is the one you want to use as that's the original definition:

Type x = typeof(MyCustomClass<>);
Type argument = x.GetGenericArguments()[0];
Type attribute = argument.CustomAttributes.First().AttributeType;
Console.WriteLine(attribute);