Read the Value of an Attribute of a Method

Read the value of an attribute of a method

You need to call the GetCustomAttributes function on a MethodBase object.

The simplest way to get the MethodBase object is to call MethodBase.GetCurrentMethod. (Note that you should add [MethodImpl(MethodImplOptions.NoInlining)])

For example:

MethodBase method = MethodBase.GetCurrentMethod();
MyAttribute attr = (MyAttribute)method.GetCustomAttributes(typeof(MyAttribute), true)[0] ;
string value = attr.Value; //Assumes that MyAttribute has a property called Value

You can also get the MethodBase manually, like this: (This will be faster)

MethodBase method = typeof(MyClass).GetMethod("MyMethod");

Read the value of an attribute from a method with parameters

At the root, this is a NullReferenceException problem.

That call to GetRuntimeMethod is saying "give me a method by this name with no parameters". It's returning null because the method you want has parameters. It works when you remove the parameter because then it matches the "no parameters" condition.

If you want specific parameter types, specify them, e.g. new Type[] { typeof(string) }.

If you want any number and type of parameters, use the GetMethod overload that doesn't take a Type[] (assuming there's only one method by that name, otherwise you'll get a different exception) or use GetMethods and find the method you want from the array it returns.

How to read attribute parameters and values (class attributes or method attributes)?

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.

How can I read method parameter information (names and values) from an attribute class in .NET

So from the comments and research, what I am trying to do here is impossible with current built-in .Net framework capabilities. However, I did take @Eugene's advice and passed in the parameters from the method to the attribute to build the dynamic route. Ended-up something like this:

[UseRestEndpoint("appointment/{first}/{last}")]
public AppointmentDto GetAppointmentById(string first, string last)
{
return Send<AppointmentDto>(new { first, last });
}

And the Attribute call that builds the dynamic route uri from the passed in dynamic object:

public string GetDynamicEndpoint(dynamic parameters)
{
if (!Uri.Contains("{") && !Uri.Contains("}"))
return Uri;

var valueDictionary = GetUriParameterValueDictionary(parameters);

string newUri = Uri;
foreach (KeyValuePair<string, string> pair in valueDictionary)
newUri = newUri.Replace(string.Format("{{{0}}}", pair.Key), pair.Value);

return newUri;
}

private Dictionary<string, string> GetUriParameterValueDictionary(object parameters)
{
var propBag = parameters.ToPropertyDictionary();
return GetUriParameters().ToDictionary(s => s, s => propBag[s]);
}

private IEnumerable<string> GetUriParameters()
{
Regex regex = new Regex(@"(?<={)\w*(?=})");
var matchCollection = regex.Matches(Uri);

return (from Match m in matchCollection select m.Value).ToList();
}

This isn't all the implementation code for this to work, but it is what ended up getting the concept to work. Thanks everyone for the comments.

How can I retrieve the value of the Property attribute of an NUnit Test Method using System.Reflection?

You can use Attribute.GetCustomAttributes to get all information. PropertyAttribute is a little bit tricky, because you can have multiple values assigned to one key. Here is an example:

using NUnit.Framework;
using System;
using System.Reflection;

namespace ConsoleApp
{
static class Program
{
static void Main(string[] args)
{
string dllPath = @"C:\Path\To\MyDll.dll";

Assembly a = Assembly.LoadFrom(dllPath);
Type[] types = a.GetTypes();

foreach (Type type in types)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
PropertyAttribute[] propertyAttributes = (PropertyAttribute[])Attribute.GetCustomAttributes(methodInfo, typeof(PropertyAttribute));

foreach (PropertyAttribute attribute in propertyAttributes)
foreach (string key in attribute.Properties.Keys)
foreach (var value in attribute.Properties[key])
Console.WriteLine($"PropertyAttribute :: Key: {key} :: Value: {value}");

CategoryAttribute[] categoryAttributes = (CategoryAttribute[])Attribute.GetCustomAttributes(methodInfo, typeof(CategoryAttribute));

foreach (CategoryAttribute attribute in categoryAttributes)
Console.WriteLine($"CategoryAttribute :: Name: {attribute.Name}");
}
}
}
}
}

Rails - attr_accessor :- read the attribute value instead of the function overriding it

attr_accessor defines a instance variable and a method to access it (read/write).

So the easy way is to write:

def open_ledger_account
create_ledger_account!(opening_balance: @opening_balance)
end

The read_attribute would only work if opening_balance was an attribute in the database on Customer.

Read attribute value of derived class

Attributes is a properties of a class, so you don't need to create an instance of this class to get those values. You can create a separate helper-class for this:

internal static class DBHelpers
{
public static string GetTableName<TDBItem>()
where TDBItem : DBItem
{
var attribute = typeof(TDBItem).GetCustomAttribute<DBItemAttribute>();
return attribute?.TableName;
}
}

So now you can use it like this:

var tableName1 = DBHelpers.GetTableName<DBHelpers>();
var tableName2 = DBHelpers.GetTableName<Book>();

Cannot read Custom Attributes

You haven't declared the attribute on the class, you defined it on the method HelpCommand. You will have to get the MethodInfo for HelpCommand and call GetCustomAttribute on that. Something along these lines.

MethodInfo method = type.GetMethod("HelpCommand");
UserLevel userLevel = method.GetCustomAttribute<UserLevel>();

How to use methods defined in Attributes?

Attributes are dumb

Attributes don't do anything by themselves, except hold data. The only reason attributes like FilterAttribute do anything is because the MVC framework looks for them and invokes them before or after calling the method they are applied to.

How MVC invokes them

Here is what it looks like in the MVC reference code base:

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */)
{
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};

// need to reverse the filter list because the continuations are built up backward
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation, (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
return thunk();
}

Sort of hard to follow without context, and the loop is hard to spot (they use a bit of LINQ and the Aggregate method to invoke each filter in the collection) but suffice it to say that MVC will look at the method, look for attributes of a certain kind, and invoke them before and after the actual action method invocation. So it's not automatic.

How you can do it

You could do something similar, if you're willing to write code to invoke the attribute. Here's an example how to do it.

First, we define out custom attribute. This custom attribute will output a message to the console when its OnBeforeExecute method is invoked.

public class HelloWorldAttribute : System.Attribute  
{
private string _someString;

public HelloWorldAttribute(string text)
{
_someString = text;
}

public void OnBeforeExecute()
{
Console.WriteLine("OnBeforeExecute says '{0}'.", _someString);
}
}

Now we write a class that applies the attribute to one of its methods:

public class MyClass
{
[HelloWorld("This is a test!!")]
public void MyClassMethod(string text)
{
Console.WriteLine("MyClassMethod says '{0}'", text);
}
}

Now by itself, the attribute does nothing at run-time:

public class Program
{
public static void Main()
{
//By itself, the attribute does nothing
var c = new MyClass();
c.MyClassMethod("Main call");
}
}

Output:

MyClassMethod says 'Main call'

But if we write code to look for the attribute and invoke it, we can get the message to appear. This is a very simple example:

public class Program
{
public static void Main()
{
var c = new MyClass();
typeof(MyClass)
.GetMethod(nameof(c.MyClassMethod))
.GetCustomAttributes(true)
.OfType<HelloWorldAttribute>()
.First()
.OnBeforeExecute();
c.MyClassMethod("Main call");
}
}

Output:

OnBeforeExecute says 'This is a test!!'.
MyClassMethod says 'Main call'

Full example on Fiddle.



Related Topics



Leave a reply



Submit