如何从Action <t>?</t>获取方法的自定义属性

时间:2011-01-27 22:06:49

标签: c# reflection

如何从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>

3 个答案:

答案 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);
}