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
Count the Items from a Ienumerable<T> Without Iterating
Find If Current Time Falls in a Time Range
How to Mock Out the File System in C# for Unit Testing
Centering Controls Within a Form in .Net (Winforms)
Programmatically Add Column & Rows to Wpf Datagrid
Showing Difference Between Two Datetime Values in Hours
How Can a Word Document Be Created in C#
Select Multiple Items from a Datagrid in an Mvvm Wpf Project
Is Using a Mutex to Prevent Multiple Instances of the Same Program from Running Safe
Reference Assignment Is Atomic So Why Is Interlocked.Exchange(Ref Object, Object) Needed
System.Outofmemoryexception' Was Thrown When There Is Still Plenty of Memory Free
How to Get the Propertyinfo of a Specific Property
Initializing C# Auto-Properties
Operator Overloading with C# Extension Methods
Get the Serial Number of Usb Storage Devices in .Net Core 2.1