如果我有Expression<Func<Delegate>>
形式的表达式,是否可以确定用于传递委托的对象的派生类型?该表达式是否包含此信息,或者它是否仅代表该代表。
代码示例应该让事情更清楚。
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Sandbox {
public interface IContract {
void Method();
}
public class Concrete : IContract {
public void Method() { }
}
public class Test {
private IContract contract = new Concrete();
private Concrete concrete = new Concrete();
public static string GetFullMethodName(Expression<Func<Action>> expression) {
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo)methodInfoExpression.Value;
var type = methodInfo.DeclaringType;
var name = methodInfo.Name;
return String.Format("{0}.{1}", type, name);
}
public Test() {
var strConcrete = GetFullMethodName(() => concrete.Method); // Sandbox.Concrete.Method
var strContract = GetFullMethodName(() => contract.Method); // Sandbox.IContract.Method
}
}
}
是否可以使() => contract.Method
生成
Sandbox.Concrete.Method
而不是
Sandbox.IContract.Method
是否可以更改表达式以支持此操作,或者我是否会被迫将对象的实例作为单独的参数传递以确定它的类型?
答案 0 :(得分:2)
不,这是不可能的。
表达式树是基于编译时信息构建的,在编译时,被调用的方法是虚拟IContract.Method
。在运行时,Concrete.Method
实际上被调用的事实是无关紧要的 - 这是在虚拟调度机制中处理的,并且编译器在完成任何任务之前很久就完成了它的工作。
您可以做的最好的事情就是尝试自己模拟虚拟调度。例如,您可以尝试在运行时枚举this
实例上的接口实现,如果找到与该接口匹配的方法,则返回该接口。但是,这肯定很棘手 - 您需要能够完美地模拟实际的虚拟调度。
另一个棘手的问题是,你试图获得价值的方式已经被破坏了。例如,在我的环境中,methodInfoExpression
不 a ConstantExpression
- 它是闭包类型的字段。你的“解析”非常脆弱。
也就是说,它也有点不必要和过于复杂 - 你试图接收一个返回动作的表达式,并从表达式中“窃取”动作的值。已经有一种简单的方法 - 直接传递该参数。无需将其隐藏在表达式树中:
public static string GetFullMethodName(Action action)
{
return string.Format("{0}.{1}", action.Method.DeclaringType.Name, action.Method.Name);
}
var strConcrete = GetFullMethodName(concrete.Method).Dump(); // Sandbox.Concrete.Method
var strContract = GetFullMethodName(contract.Method).Dump(); // Sandbox.Concrete.Method
你不需要基本反射已经给你的东西的表达式:)