• (转)lambda表达式的解析(六) 成员访问表达式


    成员访问的解析稍微有点复杂,从字符串角度看,访问一个实例的成员有三种形式:

    访问成员字段或属性 instance.field instance.property

    访问索引器 instance[]

    访问方法 instance.method(...)

    在解析的时候就按这三种形式进行解析,目前由于grammar修改的不完美所以还不支持显示进行泛型方法的调用。

    1. private Expression ProcessMemberAccessExpression(ParseTreeNode expNode)
    2. {
    3. Expression self = null;
    4. ParseTreeNode args;
    5. List<Expression> arglist;
    6. var identifier = expNode.GetDescendant("Identifier");
    7. var members = expNode.LastChild;
    8. var variableName = identifier.GetValue();
    9. var parameter = _parameters.Count > 0 ? _parameters.Peek().FirstOrDefault(p => p.Name == variableName) : null;
    10. if (parameter != null)
    11. {
    12. self = parameter;
    13. }
    14. else
    15. {
    16. var pair = _knownVariables.FirstOrDefault(p => p.Key == variableName);
    17. if (pair.Key == variableName)
    18. {
    19. self = Expression.Constant(pair.Value);
    20. //self = Expression.Variable(pair.Value.GetType(), variableName);
    21. }
    22. else if (_parameters.Count > 0)
    23. {
    24. var parameters = _parameters.Peek();
    25. var usedParameter = parameters.FirstOrDefault(p => p.Type.GetMember(variableName).Length > 0);
    26. if (usedParameter != null)
    27. self = Expression.MakeMemberAccess(usedParameter, usedParameter.Type.GetMember(variableName).First());
    28. }
    29. if (self == null)
    30. {
    31. throw new Exception(variableName);
    32. }
    33. }
    34. if (members.ChildNodes.Count == 0)
    35. {
    36. return self;
    37. }
    38. foreach (var child in members.ChildNodes)
    39. {
    40. var type = self.Type;
    41. var member = child.LastChild;
    42. MemberInfo membinfo;
    43. switch (member.GetName())
    44. {
    45. case "Identifier":
    46. membinfo = type.GetMember(member.GetValue()).First();
    47. self = Expression.MakeMemberAccess(self, membinfo);
    48. break;
    49. case "member_invoke":
    50. var methodName = member.FirstChild.GetValue();
    51. var method = type.GetMethod(methodName);
    52. args = member.GetDescendant("argument_list");
    53. arglist = new List<Expression>();
    54. if (args != null)
    55. {
    56. foreach (var arg in args.ChildNodes)
    57. {
    58. arglist.Add(ProcessExpression(arg.FirstChild));
    59. }
    60. }
    61. if (method == null)
    62. {
    63. method = MakeMethod(methodName, new[] { self.GetElementType() }.Union(arglist.Select(arg => arg.Type)).ToArray());
    64. self = Expression.Call(method, new[] { self }.Union(arglist));
    65. }
    66. else
    67. {
    68. self = Expression.Call(self, method, arglist);
    69. }
    70. break;
    71. case "member_indexer":
    72. var indexer = type.GetProperty(member.FirstChild.GetValue());
    73. args = member.GetDescendant("expression_list");
    74. arglist = new List<Expression>();
    75. foreach (var arg in args.ChildNodes)
    76. {
    77. arglist.Add(ProcessExpression(arg.FirstChild));
    78. }
    79. self = Expression.MakeIndex(self, indexer, arglist);
    80. break;
    81. default:
    82. throw new Exception(member.GetName());
    83. }
    84. }
    85. return self;
    86. }


    所有访问的成员都作为子节点进行访问,而实例则是通过查找之前介绍过的_knownVariables以及_parameters获得的。

    成员字段和索引器访问都是可以直接分析生成得到,成员方法的情况稍微复杂点,首先是通过Type.GetMethod查找类型下的指定方法,因为暂时不考虑泛型方法,所以找到后就直接生成表达式不需要做MakeGenericMethod操作。假如没有找到同名方法的话,则直接推断为该方法是一个扩展方法,进行扩展方法调用,代码里对扩展方法的处理方式是事先注册好扩展方法,然后按名字查找对应的方法进行使用。

    1. private static ConcurrentDictionary<string, MethodInfo> extensionMethods = new ConcurrentDictionary<string, MethodInfo>();
    2. #region ExtensionMethods
    3. extensionMethods["select"] = typeof(Queryable).GetExtensionMethod("Select", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    4. extensionMethods["let"] = extensionMethods["select"];
    5. extensionMethods["from"] = typeof(Queryable).GetExtensionMethod("SelectMany", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));
    6. //extensionMethods["from"] = typeof(Queryable).GetMember("SelectMany")[3] as MethodInfo;
    7. extensionMethods["group"] = typeof(Queryable).GetExtensionMethod("GroupBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    8. extensionMethods["join"] = typeof(Queryable).GetExtensionMethod("Join", typeof(IQueryable<>), typeof(IEnumerable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));
    9. extensionMethods["groupjoin"] = typeof(Queryable).GetExtensionMethod("GroupJoin", typeof(IQueryable<>), typeof(IEnumerable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));
    10. extensionMethods["orderby"] = typeof(Queryable).GetExtensionMethod("OrderBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    11. extensionMethods["orderbydescending"] = typeof(Queryable).GetExtensionMethod("OrderByDescending", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    12. extensionMethods["thenby"] = typeof(Queryable).GetExtensionMethod("ThenBy", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    13. extensionMethods["thenbydescending"] = typeof(Queryable).GetExtensionMethod("ThenByDescending", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    14. extensionMethods["where"] = typeof(Queryable).GetExtensionMethod("Where", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
    15. extensionMethods["DefaultIfEmpty"] = typeof(Enumerable).GetExtensionMethod("DefaultIfEmpty", typeof(IEnumerable<>));
    16. #endregion
    17. private static MethodInfo MakeMethod(string methodName, params Type[] genericTypes)
    18. {
    19. var len = extensionMethods[methodName].GetGenericArguments().Length;
    20. return extensionMethods[methodName].MakeGenericMethod(genericTypes.Take(len).ToArray());
    21. }


    MakeMethod就是根据方法名和泛型列表返回相应的扩展方法,这里列出的方法都是针对linq做的,全都是泛型方法。

    通过方法名和泛型列表获得一个扩展方法需要一些技巧:

    1. internal static class TypeExtensions
    2. {
    3. private class ParameterComparer : IEqualityComparer<Type>
    4. {
    5. #region IEqualityComparer<Type> Members
    6. public bool Equals(Type x, Type y)
    7. {
    8. if (x.IsGenericParameter && y.IsGenericParameter)
    9. return true;
    10. if (x.IsGenericType && y.IsGenericType)
    11. {
    12. if (x.Name != y.Name) return false;
    13. if (x.BaseType != y.BaseType) return false;
    14. var xa = x.GetGenericArguments();
    15. var ya = y.GetGenericArguments();
    16. if (xa.Length != ya.Length) return false;
    17. var ret = xa.SequenceEqual(ya, this);
    18. return ret;
    19. }
    20. if (x.IsGenericType) return true;
    21. if (!y.IsGenericType) return true;
    22. return x.Equals(y);
    23. }
    24. public int GetHashCode(Type obj)
    25. {
    26. return obj.GetHashCode();
    27. }
    28. #endregion
    29. }
    30. public static MethodInfo GetExtensionMethod(this Type type, string name, params Type[] arguments)
    31. {
    32. var query = from method in type.GetMethods()
    33. let parameters = from p in method.GetParameters() select p.ParameterType
    34. where method.Name == name
    35. where method.IsDefined(typeof(ExtensionAttribute), false)
    36. where parameters.SequenceEqual(arguments, new ParameterComparer())
    37. select method;
    38. return query.FirstOrDefault();
    39. }
    40. }


    简单点说就是通过反射获得扩展方法类下的所有方法,然后先按照名字匹配到一组方法,接着根据泛型参数来比较获得特定的那个,这里有个小的问题没解决,所以在实现IEqualityComparer<T>时代码比较冗余。小问题是

    Func<T> 的泛型类型可以通过 typeof(Func<>)得到

    而Func<T1,T2>泛型类型则是 typeof(Func<,>)

    那么Func<T1,IEnumerable<T2>,T3>这种泛型类型如何获得? typeof(Func<,,>)无法匹配 而typeof(Func<,IEnumerable<>,>)无法通过编译。

  • 相关阅读:
    防止vue文件中的样式出现‘污染’情况(html5 scoped特性)
    Vue.js中滚动条加载更多数据
    本地上传文件至服务器的技巧(linux文件压缩及解压文件)
    ubuntu下apache新建虚拟主机
    laravel5.4+vue+element简单搭建(gulp+laravel Elixir)
    java基础---->Java的格式化输出
    markdown 基本语法(转载)
    谷歌断点调试
    mac 远程连接 云服务器
    棋盘覆盖问题
  • 原文地址:https://www.cnblogs.com/liuhaili/p/2210992.html
Copyright © 2020-2023  润新知