#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