lambda表達式的解析(六) 成員訪問表達式

成員訪問的解析稍微有點複雜,從字符串角度看,訪問一個實例的成員有三種形式:

訪問成員字段或屬性 instance.field instance.property

訪問索引器 instance[]

訪問方法 instance.method(...)

在解析的時候就按這三種形式進行解析,目前由於grammar修改的不完美所以還不支持顯示進行泛型方法的調用。

        private Expression ProcessMemberAccessExpression(ParseTreeNode expNode)
        {
            Expression self = null;
            ParseTreeNode args;
            List<Expression> arglist;

            var identifier = expNode.GetDescendant("Identifier");
            var members = expNode.LastChild;
            var variableName = identifier.GetValue();
            var parameter = _parameters.Count > 0 ? _parameters.Peek().FirstOrDefault(p => p.Name == variableName) : null;
            if (parameter != null)
            {
                self = parameter;
            }
            else
            {
                var pair = _knownVariables.FirstOrDefault(p => p.Key == variableName);
                if (pair.Key == variableName)
                {
                    self = Expression.Constant(pair.Value);
                    //self = Expression.Variable(pair.Value.GetType(), variableName);
                }
                else if (_parameters.Count > 0)
                {
                    var parameters = _parameters.Peek();
                    var usedParameter = parameters.FirstOrDefault(p => p.Type.GetMember(variableName).Length > 0);
                    if (usedParameter != null)
                        self = Expression.MakeMemberAccess(usedParameter, usedParameter.Type.GetMember(variableName).First());
                } 
                
                if (self == null)
                {
                    throw new Exception(variableName);
                }
            }
            if (members.ChildNodes.Count == 0)
            {
                return self;
            }
            foreach (var child in members.ChildNodes)
            {
                var type = self.Type;
                var member = child.LastChild;
                MemberInfo membinfo;
                switch (member.GetName())
                {
                    case "Identifier":
                        membinfo = type.GetMember(member.GetValue()).First();
                        self = Expression.MakeMemberAccess(self, membinfo);
                        break;
                    case "member_invoke":
                        var methodName = member.FirstChild.GetValue();
                        var method = type.GetMethod(methodName);
                        args = member.GetDescendant("argument_list");
                        arglist = new List<Expression>();
                        if (args != null)
                        {
                            foreach (var arg in args.ChildNodes)
                            {
                                arglist.Add(ProcessExpression(arg.FirstChild));
                            }
                        }
                        if (method == null)
                        {
                            method = MakeMethod(methodName, new[] { self.GetElementType() }.Union(arglist.Select(arg => arg.Type)).ToArray());
                            self = Expression.Call(method, new[] { self }.Union(arglist));
                        }
                        else
                        {
                            self = Expression.Call(self, method, arglist);
                        }
                        break;
                    case "member_indexer":
                        var indexer = type.GetProperty(member.FirstChild.GetValue());
                        args = member.GetDescendant("expression_list");
                        arglist = new List<Expression>();
                        foreach (var arg in args.ChildNodes)
                        {
                            arglist.Add(ProcessExpression(arg.FirstChild));
                        }
                        self = Expression.MakeIndex(self, indexer, arglist);
                        break;
                    default:
                        throw new Exception(member.GetName());
                }
            }
            return self;
        }


所有訪問的成員都作爲子節點進行訪問,而實例則是通過查找之前介紹過的_knownVariables以及_parameters獲得的。

成員字段和索引器訪問都是可以直接分析生成得到,成員方法的情況稍微複雜點,首先是通過Type.GetMethod查找類型下的指定方法,因爲暫時不考慮泛型方法,所以找到後就直接生成表達式不需要做MakeGenericMethod操作。假如沒有找到同名方法的話,則直接推斷爲該方法是一個擴展方法,進行擴展方法調用,代碼裏對擴展方法的處理方式是事先註冊好擴展方法,然後按名字查找對應的方法進行使用。

        private static ConcurrentDictionary<string, MethodInfo> extensionMethods = new ConcurrentDictionary<string, MethodInfo>();

            #region ExtensionMethods
            extensionMethods["select"] = typeof(Queryable).GetExtensionMethod("Select", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["let"] = extensionMethods["select"];
            extensionMethods["from"] = typeof(Queryable).GetExtensionMethod("SelectMany", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));
            //extensionMethods["from"] = typeof(Queryable).GetMember("SelectMany")[3] as MethodInfo;
            extensionMethods["group"] = typeof(Queryable).GetExtensionMethod("GroupBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            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<,,>)));
            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<,,>)));
            extensionMethods["orderby"] = typeof(Queryable).GetExtensionMethod("OrderBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["orderbydescending"] = typeof(Queryable).GetExtensionMethod("OrderByDescending", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["thenby"] = typeof(Queryable).GetExtensionMethod("ThenBy", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["thenbydescending"] = typeof(Queryable).GetExtensionMethod("ThenByDescending", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["where"] = typeof(Queryable).GetExtensionMethod("Where", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));
            extensionMethods["DefaultIfEmpty"] = typeof(Enumerable).GetExtensionMethod("DefaultIfEmpty", typeof(IEnumerable<>));
            #endregion

        private static MethodInfo MakeMethod(string methodName, params Type[] genericTypes)
        {
            var len = extensionMethods[methodName].GetGenericArguments().Length;
            return extensionMethods[methodName].MakeGenericMethod(genericTypes.Take(len).ToArray());
        }


MakeMethod就是根據方法名和泛型列表返回相應的擴展方法,這裏列出的方法都是針對linq做的,全都是泛型方法。

通過方法名和泛型列表獲得一個擴展方法需要一些技巧:

    internal static class TypeExtensions
    {
        private class ParameterComparer : IEqualityComparer<Type>
        {

            #region IEqualityComparer<Type> Members

            public bool Equals(Type x, Type y)
            {
                if (x.IsGenericParameter && y.IsGenericParameter)
                    return true;

                if (x.IsGenericType && y.IsGenericType)
                {
                    if (x.Name != y.Name) return false;
                    if (x.BaseType != y.BaseType) return false;

                    var xa = x.GetGenericArguments();
                    var ya = y.GetGenericArguments();
                    if (xa.Length != ya.Length) return false;

                    var ret = xa.SequenceEqual(ya, this);
                    return ret;
                }

                if (x.IsGenericType) return true;
                if (!y.IsGenericType) return true;

                return x.Equals(y);
            }

            public int GetHashCode(Type obj)
            {
                return obj.GetHashCode();
            }

            #endregion
        }

        public static MethodInfo GetExtensionMethod(this Type type, string name, params Type[] arguments)
        {
            var query = from method in type.GetMethods()
                        let parameters = from p in method.GetParameters() select p.ParameterType
                        where method.Name == name
                        where method.IsDefined(typeof(ExtensionAttribute), false)
                        where parameters.SequenceEqual(arguments, new ParameterComparer())
                        select method;

            return query.FirstOrDefault();
        }
    }


簡單點說就是通過反射獲得擴展方法類下的所有方法,然後先按照名字匹配到一組方法,接着根據泛型參數來比較獲得特定的那個,這裏有個小的問題沒解決,所以在實現IEqualityComparer<T>時代碼比較冗餘。小問題是

Func<T> 的泛型類型可以通過 typeof(Func<>)得到

而Func<T1,T2>泛型類型則是 typeof(Func<,>)

那麼Func<T1,IEnumerable<T2>,T3>這種泛型類型如何獲得? typeof(Func<,,>)無法匹配 而typeof(Func<,IEnumerable<>,>)無法通過編譯。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章