如何从Action<T>
委托获取方法的自定义属性?
示例:
//simple custom attribute
public class StatusAttribute : Attribute
{
public string Message { get; set; } = string.Empty;
}
// an extension methodto wrap MethodInfo.GetCustomAttributes(Type, Bool) with
// generics for the custom Attribute type
public static class MethodInfoExtentions
{
public static IEnumerable<TAttribute> GetCustomAttributes<TAttribute>(this MethodInfo methodInfo, bool inherit) where TAttribute : Attribute
{
object[] attributeObjects = methodInfo.GetCustomAttributes(typeof(TAttribute), inherit);
return attributeObjects.Cast<TAttribute>();
}
}
// test class with a test method to implment the custom attribute
public class Foo
{
[Status(Message="I'm doing something")]
public void DoSomething()
{
// code would go here
}
}
// creates an action and attempts to get the attribute on the action
private void CallDoSomething()
{
Action<Foo> myAction = new Action<Foo>(m => m.DoSomething());
IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true);
// Status Attributes count = 0? Why?
}
我意识到我可以通过在Foo上使用反射来做到这一点,但是对于我想要创建的内容,我必须使用Action<T>
。
答案 0 :(得分:11)
问题是该操作不直接指向Foo.DoSomething
。它指向编译器生成的形式的方法:
private static void <>__a(Foo m)
{
m.DoSomething();
}
此处的一个选项是将其更改为Expression<Action<T>>
,然后您可以解剖表达式树并提取属性:
Expression<Action<Foo>> myAction = m => m.DoSomething();
var method = ((MethodCallExpression)myAction.Body).Method;
var statusAttributes = method.GetCustomAttributes<StatusAttribute>(true);
int count = statusAttributes.Count(); // = 1
答案 1 :(得分:3)
问题是lambda m => m.DoSomething()
与DoSomething
相同。它是一个lambda表达式,它被编译成编译器生成的方法的方法调用,可能使用编译器生成的类型(尽管可能不是后者,因为没有捕获的局部变量)。
从Action<Foo>
类型的实例(非静态)方法获取Foo
的非常详细的方法是:
var myAction = (Action<Foo>)Delegate.CreateDelegate(
typeof(Action<Foo>),
null, // treat method as static, even though it's not
typeof(Foo).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.Public)
);
显然,这远非理想,事实上可能无用;但值得了解;)
更新:实际上,我刚刚想到你可以编写一个快速扩展方法,使任何实例方法变得容易,你想要将其作为静态方法包装(并保持“正确”MethodInfo
):
public static class ActionEx
{
public static Action<T> ToStaticMethod<T>(this Action action)
{
if (!(action.Target is T))
{
throw new ArgumentException("Blah blah blah.");
}
return (Action<T>)Delegate.CreateDelegate(
typeof(Action<T>),
null,
action.Method
);
}
}
这将允许你这样做:
Action<Foo> myAction = new Action(new Foo().DoSomething).ToStaticMethod<Foo>();
不可否认,它不如m => m.DoSomething()
那么好;但它确实为您提供了Action<T>
,其Method
属性实际上直接引用了DoSomething
方法。
或者,您可以使用Action<T>
而不是Expression<Action<T>>
,并从中获取MethodInfo
。请注意,在这种情况下语法看起来是一样的:
Action<Foo> myAction = m => m.DoSomething();
Expression<Action<Foo>> myExpression = m => m.DoSomething();
但这是一个棘手的主张,因为不能保证任意Expression<Action<T>>
像m => m.DoSomething()
一样简单。
答案 2 :(得分:-1)
以前的答案都没有(除了没有用户代码的@Marc Gravell♦)似乎是可编译的:)
所以我建议我的:
private static void CallDoSomething()
{
var f = new Foo();
Action myAction = f.DoSomething;
IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true);
}