Search code examples
c#genericsreflectioncastingattributes

Getting generic attribute from a property using reflection without knowing the generic type


C# 11 has introduced generic attributes. Is it possible to get the attributes that are attached to a PropertyInfo using the GetCustomAttribute method (or similar) without knowing it's generic type, particularly where the attribute inherits from another?

Here's what I have:

public class MyTypedAttribute<TValue> : Attribute
{ 
    public virtual TValue ProcessValue(object value) { }
}

public class MyNumberAttribute : MyTypedAttribute<int>
{ 
    public override int ProcessValue(object value) { }
}

public class MyStringAttribute : MyTypedAttribute<string>
{ 
    public override string ProcessValue(object value) { }
}

My class has properties with a range of these attributes on them, and I want to get them using reflection. I would normally use the generic PropertyInfo.GetCustomAttribute<> function, but this doesnt seem to work when the attributes themselves also expect a generic parameter (unless the generic type is known at compile time, which in my case they it's not)

PropertyInfo myProp = ...;
var attr = myProp.GetCustomAttribute<MyTypedAttribute<>>();

Compile error: Unexpected use of an unbound generic name

It seems I can use the non-generic equivalent function, but the returned attribute is of type Attribute?, and it seems I cannot cast this without knowing the generic type. We cannot specify empty angle brackets when casting, so a type must be specified, but I dont know what the type is. The only thing I can think of doing is using object as the generic type, but this fails at run time:

var attr = (MyTypedAttribute<object>?)myProp.GetCustomAttribute(typeof(MyTypedAttribute<>))

Runtime error: Unable to cast object of type 'MyNumberAttribute' to type 'MyTypedAttribute`1[System.Object]'.'

Essentially what I'm trying to do is get the attributes and call the ProcessValue method, capturing the returned value. Is there any way to do this?


Solution

  • You can make a non-generic base class

    public class MyTypedAttribute<TValue> : MyUntypedAttribute
    { 
        public virtual TValue ProcessValue(object value)
        {
            // whatever
        }
    
        public override object ProcessValueObject(object value) =>
            ProcessValue(value);
    }
    
    public class MyUntypedAttribute : Attribute
    { 
        public abstract object ProcessValueObject(object value);
    }
    

    Then you can do

    var attr = myProp.GetCustomAttribute<MyUntypedAttribute>();