表达式解析器添加dynamic访问及静态方法调用

来源:互联网 发布:linux vi 退出不保存 编辑:程序博客网 时间:2024/06/08 16:28

 最近碰到需要对一个匿名类的成员进行动态访问,从lambda上看就是类似

Func<dynamic, object> f = o => o.Company

这样的操作

之前的解析器无法正常解析成表达式,因为o是object,通过反射没法得到Company,需要修改ProcessMemberAccessExpression这个方法来支持dynamic object,片段:

                        if (type == typeof(object)) //Dynamic                        {                            var binder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None,                                                                            member.GetValue(),                                                                            type,                                                                            new[]                                                                            {                                                                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)                                                                            });                            self = Expression.Dynamic(binder, type, self);                        }

目前只对property/field的访问支持dyanmic,method和indexer都没添加支持,如果谁有需求可以自行修改添加。


还有一个需求就是需要调用静态方法,类似

Func<dynamic, bool> f = o => String.IsNullOrEmpty(o.Company)

之前的解析器也没法正常工作,因为String被认为是一个变量,对静态方法支持也需要修改ProcessMemberAccessExpression这个方法。

先添加了一个With方法

        public ExpressionParser With(Type type)        {            knownTypes.TryAdd(type.Name, type);            return this;            }


把需要调用静态方法的类先注册好,然后在ProcessMemberAccessExpression内

                if (self == null)                    type = knownTypes.ContainsKey(variableName) ? knownTypes[variableName] : Type.GetType(variableName) ?? Type.GetType("System." + variableName);                else                    type = self.Type;


由于self应该是变量或者常量,如果都没有,则判断为引用了一个类型,这样就能在接下来的调用中正确执行了。

完整的ProcessMemberAccessExpression代码:

        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 (members.ChildNodes.Count == 0)            {                if (self == null)                    throw new Exception(variableName);                return self;            }            foreach (var child in members.ChildNodes)            {                Type type;                if (self == null)                    type = knownTypes.ContainsKey(variableName) ? knownTypes[variableName] : Type.GetType(variableName) ?? Type.GetType("System." + variableName);                else                    type = self.Type;                if (type == null)                    throw new Exception(variableName);                var member = child.LastChild;                MemberInfo membinfo;                switch (member.GetName())                {                    case "Identifier":                        if (type == typeof(object)) //Dynamic                        {                            var binder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None,                                                                            member.GetValue(),                                                                            type,                                                                            new[]                                                                            {                                                                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)                                                                            });                            self = Expression.Dynamic(binder, type, self);                        }                        else                         {                            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                        {                            var parameters = method.GetParameters();                            for (int i = 0; i < parameters.Length; ++i)                            {                                if (parameters[i].ParameterType != arglist[i].Type)                                {                                    arglist[i] = ConvertType(arglist[i], parameters[i].ParameterType);                                }                            }                            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;        }


 完整代码下载:

http://tinynetevent.googlecode.com/files/ExpressionParser1207.zip

原创粉丝点击