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

17 篇文章 0 订阅
14 篇文章 0 订阅

 最近碰到需要对一个匿名类的成员进行动态访问,从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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值