Возможно ли получить тип владельца метода из действия? - PullRequest
2 голосов
/ 30 июля 2010

У меня есть класс, который реализует интерфейс с некоторыми методами. У меня также есть метод с параметром Action, где я передаю метод Interface. Можно ли получить тип владельца, который имеет метод?

EDIT Это обертка:

private void Wrapper (params Action[] actions)
{
    var action = actions.FirstOrDefault();
    var type = action.Method.DeclaringType.Name;
}

private void test()
{
   Wrapper(()=> _GC.ChargeCancellation(""));
}

Для демонстрации я не перебираю коллекцию.

Я хочу тип _GC.

Ответы [ 2 ]

4 голосов
/ 30 июля 2010

На самом деле, я должен был это заметить с самого начала, но, поскольку Джон тоже этого не сделал, я не слишком расстроен из-за этого:)

Код, который вы должны начать с вашего вопроса, не компилируется:

Wrapper(() => TestClass.Hello);

Это не полный код. Вы также должны иметь:

Wrapper(TestClass.Hello);
        ^
        |
        +-- notice the missing () => here

или

Wrapper(() => TestClass.Hello());
                             ^
                             |
                             +-- notice the added parenthesis here

И теперь, когда вы отредактировали свой вопрос, стало ясно, что у вас есть вторая форма.

Между ними есть тонкая разница. Тонко для нас, но важно для компилятора:

Wrapper(TestClass.Hello);
        ^------+------^
               |
               +-- This is a method group

Wrapper(() => TestClass.Hello());
              ^------+--------^
                     |
                     +-- This is a method call

Группа методов - это ссылка на метод (или его перегрузки), вызов метода - исполняемый код.

Отличие от компилятора заключается в том, что в первом фрагменте кода компилятор заключает группу методов в действие, в основном компилируя ее так:

Wrapper(new Action(TestClass.Hello));

и, таким образом, вы передаете этот метод в метод Wrapper внутри делегата Action.

Однако вторая форма обрабатывается совсем по-другому. Теперь компилятор создает новый метод для содержания вашего кода, а затем передает новый метод в метод Wrapper вместо кода, который вы имели.

Таким образом, ваш код на самом деле выглядит так:

public static void Main()
{
    Wrapper(new Action(TempMethod1));
}

private static void TempMethod1()
{
    TestClass.Hello();
}

И именно поэтому вы видите класс формы как владельца метода, потому что это то, что он есть.

Причина, по которой я спросил в комментарии, принимаете ли вы делегата или выражение лица, заключается в том, что мой мозг работал на половине скорости. Было обнаружено что-то странное, но не вся картина.

Если вы хотите передать подобный код методу и работать с ним, у вас есть два варианта:

  1. Для делегата работайте с рефлексией, декомпилируйте код и анализируйте его, найдите вызов метода и выясните, в каком классе он сделан
  2. Для выражения проанализируйте части выражения (для этого требуется C # 3 или выше)

Поскольку ни то, ни другое не является тривиальным, я не собираюсь публиковать здесь какой-либо код, достаточно сказать, что то, что вы спрашиваете, требует нового вопроса. Из двух, я предлагаю вам пойти по пути выражения, если можете.

4 голосов
/ 30 июля 2010

РЕДАКТИРОВАТЬ: я неправильно прочитал пример и подумал, что он использует преобразование группы методов. Конечно, вы можете получить метод в самом делегате:

public void Wrapper(Action action)
{
    MethodInfo method = action.Method;
    Type type = method.DeclaringType; // TestClass
    string name = method.Name; // Hello
}

Я не совсем уверен, какой метод будет использоваться, если вы передадите экземпляр делегата с несколькими действиями ... но в этом случае это не должно быть проблемой.

Дох, я неправильно понял вопрос. Когда вы используете лямбда-выражение, это создаст дополнительный метод в вызывающем классе - и , что - это метод, который содержит ссылку на _GC.

Если вы не хотите, чтобы это поведение, вы должны изменить Wrapper для принятия params Expression<Action>[] actions - вы можете соответствующим образом изучить деревья выражений и таким образом выяснить вызовы. Иногда это немного странно, но выполнимо. Посмотрите на Body дерева выражений, которое будет представлять вызов метода. Обратите внимание, что если вы все еще хотите выполнить действия, вы можете вызвать Compile в дереве выражений, чтобы получить делегата.

...