1

I want to read the parameters of attributes (e.g. [MyAttribute("val1", "val2"]) which are attached to a class or to a method, or want to get back the attribute object of a particular class or method. This seems to be not available for all cases.

While I was trying to implement it, i.e.

  1. Read parameters of an attribute, attached to a class. How? (see Question 1)
Parameters are the constructor parameters of the attribute, e.g.
public MyOwnAttribute(params object[] p).

  2. Get attibute object's properties, attached to a method. How? (see Question 2)
In the example attribute MyOwnAttribute, there are MyProperty1 and MyProperty2 defined. So if you apply it to
[MyOwnAttribute("World", "2")]
public void MyMethod2() {...},
how can I read them (the constructor assigns "World" to MyProperty1 and "2" to MyProperty1)?

  3. Read parameters of an attribute, attached to a method. Solved. Example: MyOwnAttribute.GetMethodAttributeValues<clsA>(nameof(clsA.MyMethod2))

  4. Get attribute object's properties, attached to a class. Solved. Example: MyOwnAttribute.GetClassAttributes(typeof(clsA)).MyProperty1

I noticed that I couldn't implement it for all 4 cases, I couldn't implement it for cases 1. and 4. The code I wrote for cases 2. and 3. is working fine (I removed it since you now have Tobias' answer for all 4 cases). The SO sidebar shows some interesting links ("Related": Link 1, Link 2, Link 3), but still I couldn't put the missing pieces together.

Consider the following custom attribute:

public class MyOwnAttribute : Attribute
{

    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }

    // simple case, to make explaining it easier
    public MyOwnAttribute(string p1 = null, string p2 = null)
    {
        MyProperty1 = p1?.ToString();
        MyProperty2 = p2?.ToString();
    }

}

Or I could have the properties defined like:

    public string[] MyProperties { get; set; }

    // allows any number of properties in constructor
    public MyOwnAttribute(params object[] p)
    {
        MyProperties = p.Select(s => s.ToString()).ToArray();
    }

But I couldn't figure out how you could implement this:

  • Question 1: How can I access the list of attribte's parameter values attached to a class?
    (from the attribute's constructor, like implemented for the methods)
    Example:
    var classAttribParamValues = MyOwnAttribute.GetClassAttributeValues(typeof(clsA));
    string.Join(' ', classAttribParamValues.Select(s => s ?? "")).Dump();

  • Question 2: How can I access attribute's properties attached to a method?
    (like implemented for the class)
    Example:
    var methodInfo = MyOwnAttribute.GetMethodAttributes<clsA>(nameof(clsA.MyMethod2));
    methodInfo.MyProperty1.Dump();

Does anyone have an idea how that can be done? Many thanks in advance.

10
  • PropertyInfo class for properties and Assembly class for method metadata Commented Oct 8, 2021 at 13:30
  • Added samples to the 2 questions so you can better see what I am asking ... Commented Oct 8, 2021 at 13:51
  • 1
    I reread this question multiple times but still have no idea what you need. Maybe clarify a bit with an example. "How can I access the list of attribte's parameter values attached to a class" - seems you already do that in your code with clsInfo.MyProperty1.Dump();... Commented Oct 12, 2021 at 17:14
  • I suppose the line stating ".Where(w => w.GetType() == typeof(MyOwnAttribute)))" should be changed to ".Where(w => w.GetType() == typeof(T)))" instead? And how did you manage to create a generic class deriving from Attribute, per the spec that's not allowed. Commented Oct 13, 2021 at 5:51
  • 1
    I don't understand the difference between "Get attibute object's properties, attached to a class" and "Read parameters of an attribute, attached to a class. How?". You already got attribute object attached to a class, you have access to it's properties. Which "parameters" you mean? Commented Oct 13, 2021 at 8:59

1 Answer 1

3
+100

Edit:

After some clarifications here's what I suspect you want to have:

public static class AttributeHelper {

    public static TAttribute GetClassAttribute<TTarget, TAttribute>() where TAttribute : Attribute
        => typeof(TTarget).GetAttribute<TAttribute>();

    public static object[] GetClassAttributeValues<TTarget, TAttribute>() where TAttribute : Attribute
        => typeof(TTarget).GetAttributeValues<TAttribute>();

    public static TAttribute GetMethodAttribute<TTarget, TAttribute>(string methodName) where TAttribute : Attribute
        => typeof(TTarget).GetMethod(methodName)?.GetAttribute<TAttribute>();

    public static object[] GetMethodAttributeValues<TTarget, TAttribute>(string methodName) where TAttribute : Attribute
        => typeof(TTarget).GetMethod(methodName)?.GetAttributeValues<TAttribute>();

    private static TAttribute GetAttribute<TAttribute>(this MemberInfo memberInfo) where TAttribute : Attribute
        => memberInfo?.GetCustomAttributes(true).OfType<TAttribute>().FirstOrDefault();

    private static object[] GetAttributeValues<TAttribute>(this MemberInfo memberInfo) where TAttribute : Attribute
        => memberInfo
            ?.GetCustomAttributesData()
            .FirstOrDefault(d => d.AttributeType == typeof(TAttribute))
            ?.ConstructorArguments
            .Select(argument => argument.Value)
            .SelectMany(a => a is ReadOnlyCollection<CustomAttributeTypedArgument> p ? p.Select(c => c.Value) : new[] { a })
            .ToArray();
}

The methods are all static now and AttributeHelper no longer derives from Attribute, as you would have to provide the attribute type anyway as a parameter to the methods. As a second parameter simply provide the class on which you expect the attribute.

I took some time to reorganize the code as it was quite hard to read for me. Perhaps it's a little bit easier to read for you as well.

Example calls:

AttributeHelper.GetClassAttributeValues<clsA, MyOwnAttribute>().Dump("1.");

AttributeHelper.GetMethodAttribute<clsA, 
                        MyOwnAttribute>(nameof(clsA.MyMethod2)).Dump("2.");

AttributeHelper.GetMethodAttributeValues<clsA,
                        MyOwnAttribute>(nameof(clsA.MyMethod2)).Dump("3.");

AttributeHelper.GetClassAttribute<clsA,MyOwnAttribute>().Dump("4.");

Old Answer below:

I think what you are trying to achieve could be solved with an additional protected abstract method in AttributeHelper with a return type of string[], this would make AttributeHelper abstract as well, but that should be no problem.

Your deriving Attributes would be forced to implement this new method (if they are not abstract themselves). The Implementation would have to return an array of the values of the attributes properties.

Another approach could be to iterate over the PropertyInfos of your Attribute type and read the Properties that way.

Sign up to request clarification or add additional context in comments.

4 Comments

Tobias, which of the 2 questions are you referring to? Question 1, I assume? Then it would be a workaround like using implementation 4. and enforcing a property list by introducing an abstract method.
Have you thought about Question 2, too (how to access attribute's properties of a method)?
@Matt I think I know now what you want to achieve, that answer is adjusted accordingly.
Many thanks this is perfectly working! Now MyOwnAttribute derives directly from Attribute and I put samples into your answer. And you will get the bounty too, as soon as I am allowed to click ... ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.