Могу ли я использовать рефлексию для проверки кода в методе?
Я играю с 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 также делает это. Не могу сказать, как там это делается.