简体   繁体   中英

How to read custom attribute parameters and values attached to a class or method? (C#)

As you know, custom attributes can be "attached to" a class or to a method (I am not regarding restrictions here - I know you can restrict it to classes or methods).

I want to "read" the parameters of attributes whether they are attached to a class or to a method, or I want to get back the attribute object of a particular class or method.

While I was trying to implement it, ie

1. Read parameters of an attribute, attached to a class. How? (see Question 1)
Parameters are the constructor parameters of the attribute, eg
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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM