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