这篇是这个系列的最后一篇了,对于BlockExpression不再深入展开了,只对之前的泛型调用支持及typeof操作补漏。这次是进一步完善了grammar,现在能正常解析泛型调用及typeof操作,这使得我们能对这2种表达式进行正确的转换了。大家可以从http://tinynetevent.googlecode.com/files/ExpressionParser0809.zip下载到最新的代码。
由于对grammar作了一点修正,所以之前PareeTreeNode的扩展方法GetClrType也需要跟着改了:
- public static Type[] GetGenericArguments(this ParseTreeNode node)
- {
- var typeArgs = node.GetDescendant("type_argument_list");
- if (typeArgs == null)
- return null;
- return (from typeChild in node.GetChild("type_ref_list").ChildNodes
- select typeChild.GetClrType()).ToArray();
- }
- private static Type GetClrType(this ParseTreeNode node)
- {
- if (node == null)
- return null;
- if (node.GetName() != "qual_name_with_targs")
- node = node.GetDescendant("qual_name_with_targs");
- var isNullable = node.GetChild("qmark_opt").FindTokenAndGetText() == "?";
- var typeName = node.FindTokenAndGetText();
- var type = ExpressionParser.GetType(typeName);
- var typeArguments = node.GetGenericArguments();
- if (typeArguments != null)
- type.MakeGenericType(typeArguments);
- if (isNullable)
- return typeof(Nullable<>).MakeGenericType(type);
- return type;
- }
- public static Type GetNodeClrType(this ParseTreeNode node)
- {
- return node.GetDescendant("qual_name_with_targs").GetClrType();
- }
泛型调用时肯定是带完整的泛型参数的,比如func<int, int>(a),所以只需要能正确得到泛型参数列表就能通过反射生成正确的带实参泛型方法,成员访问表达式里关于方法调用表达式修改后的代码片段:
- case "member_invoke":
- var methodName = member.FirstChild.GetValue();
- var method = type.GetMethod(methodName);
- var targs = member.GetGenericArguments();
- if (targs != null)
- {
- method = method.MakeGenericMethod(targs);
- }
至于typeof操作则略有不同,首先typeof是在静态编译时就确定的值,所以可以看做一个ConstantExpression。然后typeof操作可以获得带泛型参数的泛型类型,也就是可以typeof(Func<,>),所以解析的时候针对泛型需要两种方式都支持。typeof视为一元运算符,解析的入口为之前介绍的一元运算符处理方法:
- private Expression ProcessUnaryExpression(ParseTreeNode expNode)
- {
- string op;
- var first = expNode.FirstChild;
- var second = expNode.LastChild;
- switch (first.GetName())
- {
- ....
- case "typeof_expression":
- return ProcessTypeofExpression(first);
- ....
- }
typeof处理方法:
- private Expression ProcessTypeofExpression(ParseTreeNode expNode)
- {
- var node = expNode.GetChild("typeof_parenthesized_expression").GetDescendant("type_ref_typeof");
- var glist = node.GetDescendant("type_argument_list_opt");
- Type type = node.GetNodeClrType();
- if (glist != null)
- {
- type = Type.GetType(String.Format("{0}`{1}", type.FullName, glist.ChildNodes.Count));
- }
- var rank = node.GetChild("rank_specifiers_opt");
- var ranks = rank.GetDescendant("rank_specifiers");
- if (ranks != null)
- {
- for (int i = ranks.ChildNodes.Count - 1; i >= 0; i--)
- {
- var r = ranks.ChildNodes[i];
- var rs = r.ChildNodes.Count;
- type = rs > 1 ? type.MakeArrayType(rs) : type.MakeArrayType();
- }
- }
- return Expression.Constant(type);
- }
对于typeof(Func<,>)这种表达式,Func<,>类型就是Func`2,也就是类型名跟一个`符号再跟上参数个数。
而对于typeof(int[][,][,,])这种形式就稍微有点疑惑了,看上去int[][,][,,]的类型应该是Int32[][,][,,],但实际上却是Int32[,,][,][],正好反一反,所以在转换的时候也只能从最后一个开始往前做MakeArrayType了,而且MakeArrayType()与MakeArrayType(1)居然也不一样,因此还需要判断是否是第一个。对C#这个特点有兴趣的可以看看http://topic.csdn.net/u/20110805/16/d1a4226c-68d4-44be-ba65-b56a1614c942.html。
基本上到这里关于lambda表达式转换的基本知识都讲完了,基于这些的基础更进一步就是如何把LINQ转换成表达式,也就是说用手工代码把LINQ的查询语法糖解析成表达式,当然提供下载的代码里已经实现了这个,下次就是要讲讲其工作原理。