Есть ли способ конвертировать MethodBody (или другую технику отражения) в дерево System.Linq.Expressions.Expression?Преобразование методаBody в дерево выражений
ответ
Да, это возможно ... но пока это не сделано, насколько я знаю.
Если кто делает знаю библиотеку, которая де-компилирует методы выражения деревьев, пожалуйста, дайте мне знать, или изменить вышеуказанное заявление.
Самая трудная часть того, что вы должны сделать, это написать CIL де-компилятор. То есть вам нужно будет перевести довольно низкоуровневые инструкции CIL (которые концептуально нацелены на стековый компьютер) на более высокоуровневые выражения.
Такие инструменты, как Redgate's Reflector или Telerik's JustDecompile, делают именно это, но вместо построения деревьев выражений они отображают исходный код; вы можете сказать, что они идут на один шаг дальше, поскольку деревья выражений в основном все еще языковые агностики.
Некоторые известные случаи, когда это было бы получить особенно сложно:
Вы бы иметь дело со случаями инструкций CIL, для которых нет предопределенного
Expression
узел дерева не существует; допустим,tail.call
, илиcpblk
(я немного догадываюсь). То есть вам придется создавать собственные типы узлов дерева выражений; скомпилировав их обратно в исполняемый метод, когда вы получите.Compile()
, дерево выражений может быть проблемой, потому что компилятор дерева выражений пытается разбить пользовательские узлы на стандартные узлы. Если это невозможно, то вы больше не можете скомпилировать дерево выражений, вы можете его только осмотреть.Вы попытаетесь распознать некоторые высокоуровневые конструкции, такие как блок C#
using
, и попытаться построить для него (пользовательский) узел дерева выражений? Помните, что C#using
разбивается на эквивалентtry…finally { someObj.Dispose(); }
во время компиляции, так что вы можете видеть вместоusing
, если вы размышляли над method body's CIL instructions и exception handling clauses.Таким образом, в общем, ожидайте, что вам нужно будет «распознать» определенные шаблоны кода и обобщить их на концепцию более высокого уровня.
Это действительно возможно, см DelegateDecompiler:
https://github.com/hazzik/DelegateDecompiler
ПРИМЕЧАНИЕ: Я не связан с этим проектом
Edit
Здесь основная подход, который принимает проект:
- Получить MethodInfo для метода, который требуется преобразовать
- Используйте methodInfo.GetMethodBody, чтобы получить объект MethodBody. Это содержит, среди прочего, MSIL и информация о аргументах и местных
- Пройти через инструкцию, изучить опкоды и построить соответствующие выражения
- Tie это все вместе и вернуть оптимизированное Expression
Вот фрагмент кода из проекта, который декомпилирует тело метода:
public class MethodBodyDecompiler
{
readonly IList<Address> args;
readonly VariableInfo[] locals;
readonly MethodInfo method;
public MethodBodyDecompiler(MethodInfo method)
{
this.method = method;
var parameters = method.GetParameters();
if (method.IsStatic)
args = parameters
.Select(p => (Address) Expression.Parameter(p.ParameterType, p.Name))
.ToList();
else
args = new[] {(Address) Expression.Parameter(method.DeclaringType, "this")}
.Union(parameters.Select(p => (Address) Expression.Parameter(p.ParameterType, p.Name)))
.ToList();
var body = method.GetMethodBody();
var addresses = new VariableInfo[body.LocalVariables.Count];
for (int i = 0; i < addresses.Length; i++)
{
addresses[i] = new VariableInfo(body.LocalVariables[i].LocalType);
}
locals = addresses.ToArray();
}
public LambdaExpression Decompile()
{
var instructions = method.GetInstructions();
var ex = Processor.Process(locals, args, instructions.First(), method.ReturnType);
return Expression.Lambda(new OptimizeExpressionVisitor().Visit(ex), args.Select(x => (ParameterExpression) x.Expression));
}
}
Спасибо за отзыв, ребята. В этом случае я не уверен, что включать, поскольку проект, который я связывал в целом, должен отвечать. Может быть, подытожим подход? – user3647737
Почему вы конкретно хотите использовать 'MethodBody'? – ChaosPandion
Есть ли что-то еще? Я упомянул об этом, потому что там вы получаете массив байтов IL. –
Да, есть что-то еще. Вам также понадобится 'MethodBase', так как здесь вы получаете доступ к сигнатуре метода (параметры и тип возвращаемого значения); вам понадобится это для создания экземпляра ['LambdaExpression'] (http://msdn.microsoft.com/en-us/library/system.linq.expressions.lambdaexpression.aspx). – stakx