Могу ли я использовать рефлексию для проверки кода в методе?

Я играю с API отражения C#. Я могу легко загрузить Type информация о классах, методах и т. д. в сборке, однако, теперь мне интересно, как я могу загрузить и прочитать код внутри метода?

7 ответов

Решение

Основной ответ:

Вы не можете с API отражения (System.Reflection).

Причина в том, что API-интерфейс отражения предназначен для работы с метаданными (тип классов, имя и подпись методов,...), но не на уровне данных (который будет представлять собой сам IL-поток).

Расширенный ответ:

Вы можете создавать (но не читать) IL с помощью System.Reflection.Emit (например, класс ILGenerator).

Через MethodInfo.GetMethodBody() Вы можете получить бинарный IL-поток для реализации метода. Но это обычно совершенно бесполезно само по себе.

Существуют внешние библиотеки (например, Cecil), которые вы можете использовать для чтения / изменения / добавления / удаления кода внутри метода.

Это зависит от того, что вы подразумеваете под прочтением кода. Есть 4 формы кода.

1- Исходный код, например. оригинальный C# или VB.NET - нет, вы не можете получить это с отражением
2- Символический код IL - Нет, вы не можете получить это с отражением
3- JIT-код сборки - нет, вы не можете получить это с отражением

4- байты IL, фактические байты, к которым компилируется IL, это вы можете получить.

Взгляните на MethodBase.GetMethodBody(). Например, вы можете получить байты IL, локальные переменные, фреймы исключений и т. Д. http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getmethodbody.aspx

Теперь есть библиотеки / пакеты, которые позволяют вам проверять код метода и даже декомпилировать его в Expression:

Вы вроде можете. Соответствующей функцией является MethodBase.GetMethodBody.

Это не совсем самый полезный API. Вы можете получить некоторую основную информацию о том, что находится внутри метода, и вы можете получить IL в виде байтового массива. Вот и все.

В библиотеке Mono.Cecil есть немного лучший API, который MethodDefinition класс со своим MethodBody реализация, которая содержит актуальную Instructions, так что вам не нужно интерпретировать необработанный байт-код. Тем не менее, если вы хотите получить код C# из него, как Reflector, вы будете сильно разочарованы. Кроме того, Сесил не очень хорошо задокументирован.

Если вы все еще хотите попробовать, тогда удачи.

Если вам не нужно делать это в реальном времени, взгляните на Reflector. Вы можете разобрать любую сборку.NET (включая библиотеки DLL ядра MS) и увидеть код на своем языке. Это может быть очень познавательно.

Обновление Кто-нибудь пробовал использовать Reflector на Reflector, чтобы выяснить, как это сделать?

Я хотел бы привести пример того, как можно исследовать код внутри метода. Как объясняли другие, это нелегко сделать с помощью собственного.NET Reflection API. Однако, используя API Mono.Reflection, вы можете программно дизассемблировать код, используяGetInstructions() метод и проверить его во время выполнения.

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

public static class MethodInfoUtil
{
    public static int NbOfInnerCalls(this MethodInfo mi)
    {
        return mi.GetInstructions().Count(
            instruction => instruction.OpCode.FlowControl == FlowControl.Call);
    }
}

Пример консольной программы:

class Program
{
    static int Add(int a, int b) => a + b;
    static int Doubling(int a) => Add(a, a);
    static int Quadrupling(int a) => Add(Add(a, a), Add(a, a));

    static void Main(string[] args)
    {
        Console.WriteLine("Inner method calls");
        Console.WriteLine("        Add: {0}", ((Func<int, int, int>)Add).Method.NbOfInnerCalls());
        Console.WriteLine("   Doubling: {0}", ((Func<int, int>)Doubling).Method.NbOfInnerCalls());
        Console.WriteLine("Quadrupling: {0}", ((Func<int, int>)Quadrupling).Method.NbOfInnerCalls());
    }
}

// Output:
// Inner method calls
//         Add: 0
//    Doubling: 1
// Quadrupling: 3

Нет
Эта функция намечена для следующей версии C#. Вы можете использовать CodeDom, чтобы получить больше информации, чем рефлексии, но пока не можете запросить дерево разбора.

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

Лучший вопрос, почему вы хотите?

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

Другие вопросы по тегам