成员访问的解析稍微有点复杂,从字符串角度看,访问一个实例的成员有三种形式:
访问成员字段或属性 instance.field instance.property
访问索引器 instance[]
访问方法 instance.method(...)
在解析的时候就按这三种形式进行解析,目前由于grammar修改的不完美所以还不支持显示进行泛型方法的调用。
private Expression ProcessMemberAccessExpression(ParseTreeNode expNode)
{
Expression self = null;
ParseTreeNode args;
List 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();
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();
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 extensionMethods = new ConcurrentDictionary();
#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
{
#region IEqualityComparer 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
Func
而Func
那么Func