File: System\Linq\Expressions\MemberInitExpression.cs
Web Access
Project: src\src\libraries\System.Linq.Expressions\src\System.Linq.Expressions.csproj (System.Linq.Expressions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Dynamic.Utils;
 
namespace System.Linq.Expressions
{
    /// <summary>
    /// Represents calling a constructor and initializing one or more members of the new object.
    /// </summary>
    [DebuggerTypeProxy(typeof(MemberInitExpressionProxy))]
    public sealed class MemberInitExpression : Expression
    {
        internal MemberInitExpression(NewExpression newExpression, ReadOnlyCollection<MemberBinding> bindings)
        {
            NewExpression = newExpression;
            Bindings = bindings;
        }
 
        /// <summary>
        /// Gets the static type of the expression that this <see cref="Expression"/> represents.
        /// </summary>
        /// <returns>The <see cref="System.Type"/> that represents the static type of the expression.</returns>
        public sealed override Type Type => NewExpression.Type;
 
        /// <summary>
        /// Gets a value that indicates whether the expression tree node can be reduced.
        /// </summary>
        public override bool CanReduce => true;
 
        /// <summary>
        /// Returns the node type of this Expression. Extension nodes should return
        /// ExpressionType.Extension when overriding this method.
        /// </summary>
        /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
        public sealed override ExpressionType NodeType => ExpressionType.MemberInit;
 
        /// <summary>Gets the expression that represents the constructor call.</summary>
        /// <returns>A <see cref="Expressions.NewExpression"/> that represents the constructor call.</returns>
        public NewExpression NewExpression { get; }
 
        /// <summary>Gets the bindings that describe how to initialize the members of the newly created object.</summary>
        /// <returns>A <see cref="ReadOnlyCollection{T}"/> of <see cref="MemberBinding"/> objects which describe how to initialize the members.</returns>
        public ReadOnlyCollection<MemberBinding> Bindings { get; }
 
        /// <summary>
        /// Dispatches to the specific visit method for this node type.
        /// </summary>
        protected internal override Expression Accept(ExpressionVisitor visitor)
        {
            return visitor.VisitMemberInit(this);
        }
 
        /// <summary>
        /// Reduces the <see cref="MemberInitExpression"/> 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.
        /// </summary>
        /// <returns>The reduced expression.</returns>
        public override Expression Reduce()
        {
            return ReduceMemberInit(NewExpression, Bindings, keepOnStack: true);
        }
 
        private static Expression ReduceMemberInit(
            Expression objExpression, ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack)
        {
            ParameterExpression objVar = Variable(objExpression.Type);
            int count = bindings.Count;
            Expression[] block = new Expression[count + 2];
            block[0] = Assign(objVar, objExpression);
            for (int i = 0; i < count; i++)
            {
                block[i + 1] = ReduceMemberBinding(objVar, bindings[i]);
            }
 
            block[count + 1] = keepOnStack ? (Expression)objVar : Utils.Empty;
            return Block(new[] { objVar }, block);
        }
 
        internal static Expression ReduceListInit(
            Expression listExpression, ReadOnlyCollection<ElementInit> initializers, bool keepOnStack)
        {
            ParameterExpression listVar = Variable(listExpression.Type);
            int count = initializers.Count;
            Expression[] block = new Expression[count + 2];
            block[0] = Assign(listVar, listExpression);
            for (int i = 0; i < count; i++)
            {
                ElementInit element = initializers[i];
                block[i + 1] = Call(listVar, element.AddMethod, element.Arguments);
            }
 
            block[count + 1] = keepOnStack ? (Expression)listVar : Utils.Empty;
            return Block(new[] { listVar }, block);
        }
 
        internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding)
        {
            MemberExpression member = Expression.MakeMemberAccess(objVar, binding.Member);
            return binding.BindingType switch
            {
                MemberBindingType.Assignment => Expression.Assign(member, ((MemberAssignment)binding).Expression),
                MemberBindingType.ListBinding => ReduceListInit(member, ((MemberListBinding)binding).Initializers, keepOnStack: false),
                MemberBindingType.MemberBinding => ReduceMemberInit(member, ((MemberMemberBinding)binding).Bindings, keepOnStack: false),
                _ => throw ContractUtils.Unreachable,
            };
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="newExpression">The <see cref="NewExpression"/> property of the result.</param>
        /// <param name="bindings">The <see cref="Bindings"/> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public MemberInitExpression Update(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
        {
            if (newExpression == NewExpression && bindings != null)
            {
                if (ExpressionUtils.SameElements(ref bindings!, Bindings))
                {
                    return this;
                }
            }
 
            return Expression.MemberInit(newExpression, bindings!);
        }
    }
 
    public partial class Expression
    {
        /// <summary>Creates a <see cref="MemberInitExpression"/>.</summary>
        /// <returns>A <see cref="MemberInitExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.MemberInit"/> and the <see cref="MemberInitExpression.NewExpression"/> and <see cref="MemberInitExpression.Bindings"/> properties set to the specified values.</returns>
        /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="MemberInitExpression.NewExpression"/> property equal to.</param>
        /// <param name="bindings">An array of <see cref="MemberBinding"/> objects to use to populate the <see cref="MemberInitExpression.Bindings"/> collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="newExpression"/> or <paramref name="bindings"/> is null.</exception>
        /// <exception cref="ArgumentException">The <see cref="MemberBinding.Member"/> property of an element of <paramref name="bindings"/> does not represent a member of the type that <paramref name="newExpression"/>.Type represents.</exception>
        public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings)
        {
            return MemberInit(newExpression, (IEnumerable<MemberBinding>)bindings);
        }
 
        /// <summary>Creates a <see cref="MemberInitExpression"/>.</summary>
        /// <returns>A <see cref="MemberInitExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.MemberInit"/> and the <see cref="MemberInitExpression.NewExpression"/> and <see cref="MemberInitExpression.Bindings"/> properties set to the specified values.</returns>
        /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="MemberInitExpression.NewExpression"/> property equal to.</param>
        /// <param name="bindings">An <see cref="IEnumerable{T}"/> that contains <see cref="MemberBinding"/> objects to use to populate the <see cref="MemberInitExpression.Bindings"/> collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="newExpression"/> or <paramref name="bindings"/> is null.</exception>
        /// <exception cref="ArgumentException">The <see cref="MemberBinding.Member"/> property of an element of <paramref name="bindings"/> does not represent a member of the type that <paramref name="newExpression"/>.Type represents.</exception>
        public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings)
        {
            ArgumentNullException.ThrowIfNull(newExpression);
            ArgumentNullException.ThrowIfNull(bindings);
            ReadOnlyCollection<MemberBinding> roBindings = bindings.ToReadOnly();
            ValidateMemberInitArgs(newExpression.Type, roBindings);
            return new MemberInitExpression(newExpression, roBindings);
        }
    }
}