如何从Action获取方法的自定义属性 ?

问题描述 投票:7回答:3

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

c# reflection
3个回答
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

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()一样简单。


-1
投票

以前的答案都没有(除了没有用户代码的@Marc Gravell♦)似乎可以编译:)

所以我建议我的:

private static void CallDoSomething()
{
    var f = new Foo();
    Action myAction = f.DoSomething;
    IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true);
}
© www.soinside.com 2019 - 2024. All rights reserved.