#if NET20 || NET30 || !NET_4_6 // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Dynamic.Utils; using System.Runtime.CompilerServices; using LinqInternal.Collections; namespace System.Linq.Expressions.Reimplement { /// /// Represents calling a constructor and initializing one or more members of the new object. /// [DebuggerTypeProxy(typeof(MemberInitExpressionProxy))] public sealed class MemberInitExpression : Expression { private readonly NewExpression _newExpression; private readonly ReadOnlyCollection _bindings; internal MemberInitExpression(NewExpression newExpression, ReadOnlyCollection bindings) { _newExpression = newExpression; _bindings = bindings; } /// /// Gets the static type of the expression that this represents. /// /// The that represents the static type of the expression. public override Type Type { get { return _newExpression.Type; } } /// /// Gets a value that indicates whether the expression tree node can be reduced. /// public override bool CanReduce { get { return true; } } /// /// Returns the node type of this Expression. Extension nodes should return /// ExpressionType.Extension when overriding this method. /// /// The of the expression. public override ExpressionType NodeType { get { return ExpressionType.MemberInit; } } ///Gets the expression that represents the constructor call. ///A that represents the constructor call. public NewExpression NewExpression { get { return _newExpression; } } ///Gets the bindings that describe how to initialize the members of the newly created object. ///A of objects which describe how to initialize the members. public ReadOnlyCollection Bindings { get { return _bindings; } } protected internal override Expression Accept(ExpressionVisitor visitor) { return visitor.VisitMemberInit(this); } /// /// Reduces the to a simpler expression. /// If CanReduce returns true, this should return a valid expression. /// This method is allowed to return another node which itself /// must be reduced. /// /// The reduced expression. public override Expression Reduce() { return ReduceMemberInit(_newExpression, _bindings, true); } internal static Expression ReduceMemberInit(Expression objExpression, ReadOnlyCollection bindings, bool keepOnStack) { var objVar = Variable(objExpression.Type, null); var count = bindings.Count; var block = new Expression[count + 2]; block[0] = Assign(objVar, objExpression); for (var i = 0; i < count; i++) { block[i + 1] = ReduceMemberBinding(objVar, bindings[i]); } block[count + 1] = keepOnStack ? (Expression)objVar : Empty(); return Block(new TrueReadOnlyCollection(block)); } internal static Expression ReduceListInit(Expression listExpression, ReadOnlyCollection initializers, bool keepOnStack) { var listVar = Variable(listExpression.Type, null); var count = initializers.Count; var block = new Expression[count + 2]; block[0] = Assign(listVar, listExpression); for (var i = 0; i < count; i++) { var element = initializers[i]; block[i + 1] = Call(listVar, element.AddMethod, element.Arguments); } block[count + 1] = keepOnStack ? (Expression)listVar : Empty(); return Block(new TrueReadOnlyCollection(block)); } internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding) { var member = MakeMemberAccess(objVar, binding.Member); switch (binding.BindingType) { case MemberBindingType.Assignment: return Assign(member, ((MemberAssignment)binding).Expression); case MemberBindingType.ListBinding: return ReduceListInit(member, ((MemberListBinding)binding).Initializers, false); case MemberBindingType.MemberBinding: return ReduceMemberInit(member, ((MemberMemberBinding)binding).Bindings, false); default: throw ContractUtils.Unreachable; } } /// /// Creates a new expression that is like this one, but using the /// supplied children. If all of the children are the same, it will /// return this expression. /// /// The property of the result. /// The property of the result. /// This expression if no children changed, or an expression with the updated children. public MemberInitExpression Update(NewExpression newExpression, IEnumerable bindings) { if (newExpression == NewExpression && bindings == Bindings) { return this; } return MemberInit(newExpression, bindings); } } public partial class Expression { ///Creates a . ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///An array of objects to use to populate the collection. /// /// or is null. ///The property of an element of does not represent a member of the type that .Type represents. public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings) { return MemberInit(newExpression, (IEnumerable)bindings); } ///Creates a . ///A that has the property equal to and the and properties set to the specified values. ///A to set the property equal to. ///An that contains objects to use to populate the collection. /// /// or is null. ///The property of an element of does not represent a member of the type that .Type represents. public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable bindings) { ContractUtils.RequiresNotNull(newExpression, "newExpression"); ContractUtils.RequiresNotNull(bindings, "bindings"); var roBindings = bindings.ToReadOnly(); ValidateMemberInitArgs(newExpression.Type, roBindings); return new MemberInitExpression(newExpression, roBindings); } } } #endif