C#里用来创建对象的方式有以下几种
调用构造函数 new obj(...)
初始化成员列表 new obj { f1 = xxx, f2 = xxx, ... }
创建数组 new arraytype[] { e1, e2, e3, ...}
如果是匿名对象话,创建方式只有后2种。
非匿名对象的创建:
- private Expression ProcessNewExpression(ParseTreeNode expNode)
- {
- var typeName = expNode.GetChild("qual_name_with_targs").FindTokenAndGetText();
- var args = expNode.GetChild("creation_args").GetDescendant("argument_list");
- var arglist = new List<Expression>();
- if (args != null)
- {
- foreach (var arg in args.ChildNodes)
- {
- arglist.Add(ProcessExpression(arg.FirstChild));
- }
- }
- var type = GetType(typeName);
- if (expNode.GetChild("creation_args").HasChild("array_creation_args"))
- {
- var elements = expNode.GetChild("array_initializer_opt").GetDescendant("elem_initializer_list");
- var elemlist = new List<Expression>();
- if (elements != null)
- {
- foreach (var child in elements.ChildNodes)
- {
- elemlist.Add(ProcessExpression(child.GetChild("initializer_value").FirstChild));
- }
- }
- return Expression.NewArrayInit(type, elemlist);
- }
- var newexp = Expression.New(type.GetConstructor(arglist.Select(e => e.Type).ToArray()), arglist);
- var members = expNode.GetChild("array_initializer_opt").GetDescendant("elem_initializer_list");
- var bindlist = new List<MemberBinding>();
- if (members != null)
- {
- foreach (var child in members.ChildNodes)
- {
- var memberName = child.GetChild("Identifier").GetValue();
- var expr = ProcessExpression(child.GetChild("initializer_value").FirstChild);
- bindlist.Add(Expression.Bind(type.GetMember(memberName).First(), expr));
- }
- }
- return Expression.MemberInit(newexp, bindlist);
- }
大致过程就是先获得创建对象的类型,然后分析是否有调用构造方法并获取构造参数列表,如是创建数组对象则获取元素列表并返回数组初始化表达式,最后就是如果有成员初始化列表就和new表达式一起使用。
匿名类型创建:
- private Expression ProcessNewAnonymousExpression(ParseTreeNode expNode)
- {
- if (expNode.HasChild("anonymous_array_creation_expression"))
- {
- var elements = expNode.GetDescendant("element_declarator_list");
- var elemlist = new List<Expression>();
- var elemtype = typeof(void);
- if (elements != null)
- {
- foreach (var child in elements.ChildNodes)
- {
- var exp = ProcessExpression(child.FirstChild);
- elemtype = exp.Type;
- elemlist.Add(exp);
- }
- }
- return Expression.NewArrayInit(elemtype, elemlist);
- }
- var members = expNode.GetDescendant("member_declarator_list");
- var memblist = new List<Expression>();
- var names = new List<string>();
- var types = new List<Type>();
- List<AnonymousMetaProperty> properties = new List<AnonymousMetaProperty>();
- if (members != null)
- {
- foreach (var child in members.ChildNodes)
- {
- var name = child.GetChild("Identifier").GetValue();
- var expr = ProcessExpression(child.GetChild("primary_expression"));
- names.Add(name);
- types.Add(expr.Type);
- memblist.Add(expr);
- }
- }
- return GetAnonymousExpression(names.ToArray(), types.ToArray(), memblist.ToArray());
- }
创建匿名表达式最大的问题是需要对匿名类型进行引用,也就是说需要在解析的时候动态创建匿名类型,在项目里使用了InterLinq创建匿名类型部分的代码(InterLinq是一个提供远程执行LINQ方法的框架)来创建一个匿名类型:
- private static Type GetAnonymousType(string[] propertyNames, Type[] propertyTypes)
- {
- List<AnonymousMetaProperty> properties = new List<AnonymousMetaProperty>();
- for (int i = 0; i < propertyNames.Length; ++i)
- {
- properties.Add(new AnonymousMetaProperty(propertyNames[i], propertyTypes[i]));
- }
- return AnonymousTypeHelper.GetAnonymousType(properties.ToArray()).GetClrType();
- }
- private Expression GetAnonymousExpression(IEnumerable<string> names, IEnumerable<Type> types, IEnumerable<Expression> membs)
- {
- var newobj = GetAnonymousType(names.ToArray(), types.ToArray());
- return Expression.New(newobj.GetConstructor(types.ToArray()), membs, newobj.GetProperties());
- }
不过匿名数组可以根据初始化参数确定数组元素类型所以无需创建匿名类型这个过程。
至此大部分的表达式都能被顺利解析转换了,用Irony对lambda表达式解析的介绍就告一段落了,对typeof、泛型调用以及BlockExpression等的支持和grammar的进一步完善留着下次再介绍了。