File: System\Linq\Expressions\InvocationExpression.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.Diagnostics.CodeAnalysis;
using System.Dynamic.Utils;
using System.Reflection;
 
namespace System.Linq.Expressions
{
    /// <summary>
    /// Represents an expression that applies a delegate or lambda expression to a list of argument expressions.
    /// </summary>
    [DebuggerTypeProxy(typeof(InvocationExpressionProxy))]
    public class InvocationExpression : Expression, IArgumentProvider
    {
        internal InvocationExpression(Expression expression, Type returnType)
        {
            Expression = expression;
            Type = returnType;
        }
 
        /// <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 { get; }
 
        /// <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.Invoke;
 
        /// <summary>
        /// Gets the delegate or lambda expression to be applied.
        /// </summary>
        public Expression Expression { get; }
 
        /// <summary>
        /// Gets the arguments that the delegate or lambda expression is applied to.
        /// </summary>
        public ReadOnlyCollection<Expression> Arguments => GetOrMakeArguments();
 
        /// <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="expression">The <see cref="Expression"/> property of the result.</param>
        /// <param name="arguments">The <see cref="Arguments"/> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public InvocationExpression Update(Expression expression, IEnumerable<Expression>? arguments)
        {
            if (expression == Expression && arguments != null)
            {
                if (ExpressionUtils.SameElements(ref arguments, Arguments))
                {
                    return this;
                }
            }
 
            return Invoke(expression, arguments);
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            throw ContractUtils.Unreachable;
        }
 
        /// <summary>
        /// Gets the argument expression with the specified <paramref name="index"/>.
        /// </summary>
        /// <param name="index">The index of the argument expression to get.</param>
        /// <returns>The expression representing the argument at the specified <paramref name="index"/>.</returns>
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        public virtual Expression GetArgument(int index)
        {
            throw ContractUtils.Unreachable;
        }
 
        /// <summary>
        /// Gets the number of argument expressions of the node.
        /// </summary>
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        public virtual int ArgumentCount
        {
            get
            {
                throw ContractUtils.Unreachable;
            }
        }
 
        /// <summary>
        /// Dispatches to the specific visit method for this node type.
        /// </summary>
        protected internal override Expression Accept(ExpressionVisitor visitor)
        {
            return visitor.VisitInvocation(this);
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            throw ContractUtils.Unreachable;
        }
 
        internal LambdaExpression? LambdaOperand
        {
            get
            {
                return (Expression.NodeType == ExpressionType.Quote)
                    ? (LambdaExpression)((UnaryExpression)Expression).Operand
                    : (Expression as LambdaExpression);
            }
        }
    }
 
    #region Specialized Subclasses
 
    internal sealed class InvocationExpressionN : InvocationExpression
    {
        private IReadOnlyList<Expression> _arguments;
 
        public InvocationExpressionN(Expression lambda, IReadOnlyList<Expression> arguments, Type returnType)
            : base(lambda, returnType)
        {
            _arguments = arguments;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(ref _arguments);
        }
 
        public override Expression GetArgument(int index) => _arguments[index];
 
        public override int ArgumentCount => _arguments.Count;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == _arguments.Count);
 
            return Expression.Invoke(lambda, arguments ?? _arguments);
        }
    }
 
    internal sealed class InvocationExpression0 : InvocationExpression
    {
        public InvocationExpression0(Expression lambda, Type returnType)
            : base(lambda, returnType)
        {
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ReadOnlyCollection<Expression>.Empty;
        }
 
        public override Expression GetArgument(int index)
        {
            throw new ArgumentOutOfRangeException(nameof(index));
        }
 
        public override int ArgumentCount => 0;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 0);
 
            return Expression.Invoke(lambda);
        }
    }
 
    internal sealed class InvocationExpression1 : InvocationExpression
    {
        private object _arg0;       // storage for the 1st argument or a read-only collection.  See IArgumentProvider
 
        public InvocationExpression1(Expression lambda, Type returnType, Expression arg0)
            : base(lambda, returnType)
        {
            _arg0 = arg0;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(this, ref _arg0);
        }
 
        public override Expression GetArgument(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<Expression>(_arg0),
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
 
        public override int ArgumentCount => 1;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 1);
 
            if (arguments != null)
            {
                return Expression.Invoke(lambda, arguments[0]);
            }
            return Expression.Invoke(lambda, ExpressionUtils.ReturnObject<Expression>(_arg0));
        }
    }
 
    internal sealed class InvocationExpression2 : InvocationExpression
    {
        private object _arg0;               // storage for the 1st argument or a read-only collection.  See IArgumentProvider
        private readonly Expression _arg1;  // storage for the 2nd argument
 
        public InvocationExpression2(Expression lambda, Type returnType, Expression arg0, Expression arg1)
            : base(lambda, returnType)
        {
            _arg0 = arg0;
            _arg1 = arg1;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(this, ref _arg0);
        }
 
        public override Expression GetArgument(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<Expression>(_arg0),
                1 => _arg1,
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
 
        public override int ArgumentCount => 2;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 2);
 
            if (arguments != null)
            {
                return Expression.Invoke(lambda, arguments[0], arguments[1]);
            }
            return Expression.Invoke(lambda, ExpressionUtils.ReturnObject<Expression>(_arg0), _arg1);
        }
    }
 
    internal sealed class InvocationExpression3 : InvocationExpression
    {
        private object _arg0;               // storage for the 1st argument or a read-only collection.  See IArgumentProvider
        private readonly Expression _arg1;  // storage for the 2nd argument
        private readonly Expression _arg2;  // storage for the 3rd argument
 
        public InvocationExpression3(Expression lambda, Type returnType, Expression arg0, Expression arg1, Expression arg2)
            : base(lambda, returnType)
        {
            _arg0 = arg0;
            _arg1 = arg1;
            _arg2 = arg2;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(this, ref _arg0);
        }
 
        public override Expression GetArgument(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<Expression>(_arg0),
                1 => _arg1,
                2 => _arg2,
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
 
        public override int ArgumentCount => 3;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 3);
 
            if (arguments != null)
            {
                return Expression.Invoke(lambda, arguments[0], arguments[1], arguments[2]);
            }
            return Expression.Invoke(lambda, ExpressionUtils.ReturnObject<Expression>(_arg0), _arg1, _arg2);
        }
    }
 
    internal sealed class InvocationExpression4 : InvocationExpression
    {
        private object _arg0;               // storage for the 1st argument or a read-only collection.  See IArgumentProvider
        private readonly Expression _arg1;  // storage for the 2nd argument
        private readonly Expression _arg2;  // storage for the 3rd argument
        private readonly Expression _arg3;  // storage for the 4th argument
 
        public InvocationExpression4(Expression lambda, Type returnType, Expression arg0, Expression arg1, Expression arg2, Expression arg3)
            : base(lambda, returnType)
        {
            _arg0 = arg0;
            _arg1 = arg1;
            _arg2 = arg2;
            _arg3 = arg3;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(this, ref _arg0);
        }
 
        public override Expression GetArgument(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<Expression>(_arg0),
                1 => _arg1,
                2 => _arg2,
                3 => _arg3,
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
 
        public override int ArgumentCount => 4;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 4);
 
            if (arguments != null)
            {
                return Expression.Invoke(lambda, arguments[0], arguments[1], arguments[2], arguments[3]);
            }
            return Expression.Invoke(lambda, ExpressionUtils.ReturnObject<Expression>(_arg0), _arg1, _arg2, _arg3);
        }
    }
 
    internal sealed class InvocationExpression5 : InvocationExpression
    {
        private object _arg0;               // storage for the 1st argument or a read-only collection.  See IArgumentProvider
        private readonly Expression _arg1;  // storage for the 2nd argument
        private readonly Expression _arg2;  // storage for the 3rd argument
        private readonly Expression _arg3;  // storage for the 4th argument
        private readonly Expression _arg4;  // storage for the 5th argument
 
        public InvocationExpression5(Expression lambda, Type returnType, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4)
            : base(lambda, returnType)
        {
            _arg0 = arg0;
            _arg1 = arg1;
            _arg2 = arg2;
            _arg3 = arg3;
            _arg4 = arg4;
        }
 
        internal override ReadOnlyCollection<Expression> GetOrMakeArguments()
        {
            return ExpressionUtils.ReturnReadOnly(this, ref _arg0);
        }
 
        public override Expression GetArgument(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<Expression>(_arg0),
                1 => _arg1,
                2 => _arg2,
                3 => _arg3,
                4 => _arg4,
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
 
        public override int ArgumentCount => 5;
 
        internal override InvocationExpression Rewrite(Expression lambda, Expression[]? arguments)
        {
            Debug.Assert(lambda != null);
            Debug.Assert(arguments == null || arguments.Length == 5);
 
            if (arguments != null)
            {
                return Expression.Invoke(lambda, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
            }
            return Expression.Invoke(lambda, ExpressionUtils.ReturnObject<Expression>(_arg0), _arg1, _arg2, _arg3, _arg4);
        }
    }
 
    #endregion
 
    public partial class Expression
    {
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression with no arguments.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression)
        {
            // COMPAT: This method is marked as non-public to avoid a gap between a 0-ary and 2-ary overload (see remark for the unary case below).
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 0, pis);
 
            return new InvocationExpression0(expression, method.ReturnType);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to one argument expression.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arg0">
        /// The <see cref="Expression"/> that represents the first argument.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an argument expression is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression, Expression arg0)
        {
            // COMPAT: This method is marked as non-public to ensure compile-time compatibility for Expression.Invoke(e, null).
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 1, pis);
 
            arg0 = ValidateOneArgument(method, ExpressionType.Invoke, arg0, pis[0], nameof(expression), nameof(arg0));
 
            return new InvocationExpression1(expression, method.ReturnType, arg0);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to two argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arg0">
        /// The <see cref="Expression"/> that represents the first argument.
        /// </param>
        /// <param name="arg1">
        /// The <see cref="Expression"/> that represents the second argument.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an argument expression is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression, Expression arg0, Expression arg1)
        {
            // NB: This method is marked as non-public to avoid public API additions at this point.
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 2, pis);
 
            arg0 = ValidateOneArgument(method, ExpressionType.Invoke, arg0, pis[0], nameof(expression), nameof(arg0));
            arg1 = ValidateOneArgument(method, ExpressionType.Invoke, arg1, pis[1], nameof(expression), nameof(arg1));
 
            return new InvocationExpression2(expression, method.ReturnType, arg0, arg1);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to three argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arg0">
        /// The <see cref="Expression"/> that represents the first argument.
        /// </param>
        /// <param name="arg1">
        /// The <see cref="Expression"/> that represents the second argument.
        /// </param>
        /// <param name="arg2">
        /// The <see cref="Expression"/> that represents the third argument.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an argument expression is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression, Expression arg0, Expression arg1, Expression arg2)
        {
            // NB: This method is marked as non-public to avoid public API additions at this point.
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 3, pis);
 
            arg0 = ValidateOneArgument(method, ExpressionType.Invoke, arg0, pis[0], nameof(expression), nameof(arg0));
            arg1 = ValidateOneArgument(method, ExpressionType.Invoke, arg1, pis[1], nameof(expression), nameof(arg1));
            arg2 = ValidateOneArgument(method, ExpressionType.Invoke, arg2, pis[2], nameof(expression), nameof(arg2));
 
            return new InvocationExpression3(expression, method.ReturnType, arg0, arg1, arg2);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to four argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arg0">
        /// The <see cref="Expression"/> that represents the first argument.
        /// </param>
        /// <param name="arg1">
        /// The <see cref="Expression"/> that represents the second argument.
        /// </param>
        /// <param name="arg2">
        /// The <see cref="Expression"/> that represents the third argument.
        /// </param>
        /// <param name="arg3">
        /// The <see cref="Expression"/> that represents the fourth argument.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an argument expression is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression, Expression arg0, Expression arg1, Expression arg2, Expression arg3)
        {
            // NB: This method is marked as non-public to avoid public API additions at this point.
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 4, pis);
 
            arg0 = ValidateOneArgument(method, ExpressionType.Invoke, arg0, pis[0], nameof(expression), nameof(arg0));
            arg1 = ValidateOneArgument(method, ExpressionType.Invoke, arg1, pis[1], nameof(expression), nameof(arg1));
            arg2 = ValidateOneArgument(method, ExpressionType.Invoke, arg2, pis[2], nameof(expression), nameof(arg2));
            arg3 = ValidateOneArgument(method, ExpressionType.Invoke, arg3, pis[3], nameof(expression), nameof(arg3));
 
            return new InvocationExpression4(expression, method.ReturnType, arg0, arg1, arg2, arg3);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to five argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arg0">
        /// The <see cref="Expression"/> that represents the first argument.
        /// </param>
        /// <param name="arg1">
        /// The <see cref="Expression"/> that represents the second argument.
        /// </param>
        /// <param name="arg2">
        /// The <see cref="Expression"/> that represents the third argument.
        /// </param>
        /// <param name="arg3">
        /// The <see cref="Expression"/> that represents the fourth argument.
        /// </param>
        /// <param name="arg4">
        /// The <see cref="Expression"/> that represents the fifth argument.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an argument expression is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The number of arguments does not contain match the number of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        internal static InvocationExpression Invoke(Expression expression, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4)
        {
            // NB: This method is marked as non-public to avoid public API additions at this point.
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            MethodInfo method = GetInvokeMethod(expression);
 
            ParameterInfo[] pis = GetParametersForValidation(method, ExpressionType.Invoke);
 
            ValidateArgumentCount(method, ExpressionType.Invoke, 5, pis);
 
            arg0 = ValidateOneArgument(method, ExpressionType.Invoke, arg0, pis[0], nameof(expression), nameof(arg0));
            arg1 = ValidateOneArgument(method, ExpressionType.Invoke, arg1, pis[1], nameof(expression), nameof(arg1));
            arg2 = ValidateOneArgument(method, ExpressionType.Invoke, arg2, pis[2], nameof(expression), nameof(arg2));
            arg3 = ValidateOneArgument(method, ExpressionType.Invoke, arg3, pis[3], nameof(expression), nameof(arg3));
            arg4 = ValidateOneArgument(method, ExpressionType.Invoke, arg4, pis[4], nameof(expression), nameof(arg4));
 
            return new InvocationExpression5(expression, method.ReturnType, arg0, arg1, arg2, arg3, arg4);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to a list of argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arguments">
        /// An array of <see cref="Expression"/> objects
        /// that represent the arguments that the delegate or lambda expression is applied to.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an element of <paramref name="arguments"/> is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// <paramref name="arguments"/> does not contain the same number of elements as the list of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        public static InvocationExpression Invoke(Expression expression, params Expression[]? arguments)
        {
            return Invoke(expression, (IEnumerable<Expression>?)arguments);
        }
 
        /// <summary>
        /// Creates an <see cref="InvocationExpression"/> that
        /// applies a delegate or lambda expression to a list of argument expressions.
        /// </summary>
        /// <returns>
        /// An <see cref="InvocationExpression"/> that
        /// applies the specified delegate or lambda expression to the provided arguments.
        /// </returns>
        /// <param name="expression">
        /// An <see cref="Expression"/> that represents the delegate
        /// or lambda expression to be applied.
        /// </param>
        /// <param name="arguments">
        /// An <see cref="Collections.Generic.IEnumerable{TDelegate}"/> of <see cref="Expression"/> objects
        /// that represent the arguments that the delegate or lambda expression is applied to.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="expression"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="expression"/>.Type does not represent a delegate type or an <see cref="Expression{TDelegate}"/>.-or-The <see cref="Expression.Type"/> property of an element of <paramref name="arguments"/> is not assignable to the type of the corresponding parameter of the delegate represented by <paramref name="expression"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// <paramref name="arguments"/> does not contain the same number of elements as the list of parameters for the delegate represented by <paramref name="expression"/>.</exception>
        public static InvocationExpression Invoke(Expression expression, IEnumerable<Expression>? arguments)
        {
            IReadOnlyList<Expression> argumentList = arguments as IReadOnlyList<Expression> ?? arguments.ToReadOnly();
 
            switch (argumentList.Count)
            {
                case 0:
                    return Invoke(expression);
                case 1:
                    return Invoke(expression, argumentList[0]);
                case 2:
                    return Invoke(expression, argumentList[0], argumentList[1]);
                case 3:
                    return Invoke(expression, argumentList[0], argumentList[1], argumentList[2]);
                case 4:
                    return Invoke(expression, argumentList[0], argumentList[1], argumentList[2], argumentList[3]);
                case 5:
                    return Invoke(expression, argumentList[0], argumentList[1], argumentList[2], argumentList[3], argumentList[4]);
            }
 
            ExpressionUtils.RequiresCanRead(expression, nameof(expression));
 
            ReadOnlyCollection<Expression> args = argumentList.ToReadOnly(); // Ensure is TrueReadOnlyCollection when count > 5. Returns fast if it already is.
            MethodInfo mi = GetInvokeMethod(expression);
            ValidateArgumentTypes(mi, ExpressionType.Invoke, ref args, nameof(expression));
            return new InvocationExpressionN(expression, args, mi.ReturnType);
        }
 
        /// <summary>
        /// Gets the delegate's Invoke method; used by InvocationExpression.
        /// </summary>
        /// <param name="expression">The expression to be invoked.</param>
        internal static MethodInfo GetInvokeMethod(Expression expression)
        {
            Type delegateType = expression.Type;
            if (!expression.Type.IsSubclassOf(typeof(MulticastDelegate)))
            {
                Type? exprType = TypeUtils.FindGenericType(typeof(Expression<>), expression.Type);
                if (exprType == null)
                {
                    throw Error.ExpressionTypeNotInvocable(expression.Type, nameof(expression));
                }
                delegateType = exprType.GetGenericArguments()[0];
            }
 
            return delegateType.GetInvokeMethod();
        }
    }
}