问题是 aMethodCallExpression
必须实际上是一种方法。考虑:
public static void Main()
{
Express(str => str.Length);
Console.ReadLine();
}
static void Express(Expression<Func<String, Int32>> expression)
{
// Outputs: PropertyExpression (Which is a form of member expression)
Console.WriteLine(expression.Body.GetType());
Console.ReadLine();
}
表达式是在编译时确定的,这意味着当我说str => str.Length
我正在调用一个属性时str
,编译器会将其解析为MemberExpression
.
如果我改为将我的 lambda 更改为如下所示:
Express(str => str.Count());
然后编译器知道我正在调用Count()
,str
所以它解析为MethodCallExpression
... 因为它实际上是一个方法。
但是请注意,这意味着您不能真正将表达式从一种类型“转换”为另一种类型,就像您不能将 aString
转换为Int32
. 你可以做一个解析,但我认为你明白那不是真正的对话......
...也就是说,您可以MethodCallExpression
白手起家,这在某些情况下很有帮助。例如,让我们构建 lambda:
(str, startsWith) => str.StartsWith(startsWith)
(1) 首先我们需要从构建两个参数开始:(str, startsWith) => ...
// The first parameter is type "String", and well call it "str"
// The second parameter also type "String", and well call it "startsWith"
ParameterExpression str = Expression.Parameter(typeof(String), "str");
ParameterExpression startsWith = Expression.Parameter(typeof(String), "startsWith");
(2) 然后在右手边,我们需要构建:str.StartsWith(startsWith)
. 首先,我们需要使用反射绑定到接受单个输入 type的StartsWith(...)
方法,如下所示:String
String
// Get the method metadata for "StartsWith" -- the version that takes a single "String" input.
MethodInfo startsWithMethod = typeof(String).GetMethod("StartsWith", new [] { typeof(String) });
(3) 现在我们有了绑定元数据,我们可以使用 aMethodCallExpression
来实际调用方法,如下所示:
//This is the same as (...) => str.StartsWith(startsWith);
// That is: Call the method pointed to by "startsWithMethod" bound above. Make sure to call it
// on 'str', and then use 'startsWith' (defined above as well) as the input.
MethodCallExpression callStartsWith = Expression.Call(str, startsWithMethod, new Expression[] { startsWith });
(4) 现在我们有左侧(str, startsWith)
/ 和右侧str.StartsWith(startsWith)
。现在我们只需要将它们加入到一个 lambda 中。最终代码:
// The first parameter is type "String", and well call it "str"
// The second parameter also type "String", and well call it "startsWith"
ParameterExpression str = Expression.Parameter(typeof(String), "str");
ParameterExpression startsWith = Expression.Parameter(typeof(String), "startsWith");
// Get the method metadata for "StartsWith" -- the version that takes a single "String" input.
MethodInfo startsWithMethod = typeof(String).GetMethod("StartsWith", new[] { typeof(String) });
// This is the same as (...) => str.StartsWith(startsWith);
// That is: Call the method pointed to by "startsWithMethod" bound above. Make sure to call it
// on 'str', and then use 'startsWith' (defined above as well) as the input.
MethodCallExpression callStartsWith = Expression.Call(str, startsWithMethod, new Expression[] { startsWith });
// This means, convert the "callStartsWith" lambda-expression (with two Parameters: 'str' and 'startsWith', into an expression
// of type Expression<Func<String, String, Boolean>
Expression<Func<String, String, Boolean>> finalExpression =
Expression.Lambda<Func<String, String, Boolean>>(callStartsWith, new ParameterExpression[] { str, startsWith });
// Now let's compile it for extra speed!
Func<String, String, Boolean> compiledExpression = finalExpression.Compile();
// Let's try it out on "The quick brown fox" (str) and "The quick" (startsWith)
Console.WriteLine(compiledExpression("The quick brown fox", "The quick")); // Outputs: "True"
Console.WriteLine(compiledExpression("The quick brown fox", "A quick")); // Outputs: "False"
更新
好吧,也许这样的事情可能会起作用:
class Program
{
public void DoAction()
{
Console.WriteLine("actioned");
}
public delegate void ActionDoer();
public void Do()
{
Console.ReadLine();
}
public static void Express(Expression<Func<Program, ActionDoer>> expression)
{
Program program = new Program();
Func<Program, ActionDoer> function = expression.Compile();
function(program).Invoke();
}
[STAThread]
public static void Main()
{
Express(program => program.DoAction);
Console.ReadLine();
}
}
更新:偶然发现了一些东西。考虑这段代码:
public static String SetPropertyChanged<T>(Expression<Func<T, Object>> expression)
{
UnaryExpression convertExpression = (UnaryExpression)expression.Body;
MemberExpression memberExpression = (MemberExpression)convertExpression.Operand;
return memberExpression.Member.Name;
...
}
输入是 WPF 的简单 lambda:
base.SetPropertyChanged(x => x.Visibility);
因为我正在投影到 aObject
中,所以我注意到 Visual Studio 将其转换为 a UnaryExpression
,我认为这与您遇到的问题相同。如果您放置一个断点并检查实际表达式(在我的情况下),它会显示x => Convert(x.Visibility)
. 问题是Convert
(实际上只是对当前未知类型的强制转换)。您所要做的就是删除它(就像我在上面的代码中使用该Operand
成员所做的那样,您应该已经准备就绪。也许您将拥有您的MethodCallExpression
.