Как получить пользовательские атрибуты метода из Action <T>? - PullRequest
7 голосов
/ 28 января 2011

Как я могу получить пользовательские атрибуты метода от 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 ]

11 голосов
/ 28 января 2011

Проблема в том, что действие напрямую не указывает на 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 голосов
/ 28 января 2011

Проблема в том, что лямбда m => m.DoSomething() равна , а не так же, как DoSomething.Это лямбда-выражение, которое компилируется в вызов метода для сгенерированного компилятором метода, возможно, с использованием сгенерированного компилятором типа (хотя, возможно, не последнего, поскольку нет захваченных локальных переменных).

Оченьподробный способ получения 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().

0 голосов
/ 28 января 2011

Ни один из предыдущих ответов (за исключением @Marc Gravell ♦, у которого нет кода пользователя) кажется скомпилированным:)

Итак, я бы предложил мой:

private static void CallDoSomething()
{
    var f = new Foo();
    Action myAction = f.DoSomething;
    IEnumerable<StatusAttribute> statusAttributes = myAction.Method.GetCustomAttributes<StatusAttribute>(true);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...