File: System\Linq\Expressions\LambdaExpression.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;
using System.Runtime.CompilerServices;
 
namespace System.Linq.Expressions
{
    /// <summary>
    /// Creates a <see cref="LambdaExpression"/> node.
    /// This captures a block of code that is similar to a .NET method body.
    /// </summary>
    /// <remarks>
    /// Lambda expressions take input through parameters and are expected to be fully bound.
    /// </remarks>
    [DebuggerTypeProxy(typeof(LambdaExpressionProxy))]
    public abstract class LambdaExpression : Expression, IParameterProvider
    {
        private static readonly MethodInfo s_expressionCompileMethodInfo = typeof(Expression<>).GetMethod("Compile", Type.EmptyTypes)!;
 
        private readonly Expression _body;
 
        [FeatureGuard(typeof(RequiresDynamicCodeAttribute))]
        public static bool CanCompileToIL => RuntimeFeature.IsDynamicCodeSupported;
 
        // This could be flipped to false using feature switches at publishing time
        public static bool CanInterpret => true;
 
        internal LambdaExpression(Expression body)
        {
            _body = body;
        }
 
        /// <summary>
        /// Gets the static type of the expression that this <see cref="Expression"/> represents. (Inherited from <see cref="Expression"/>.)
        /// </summary>
        /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
        public sealed override Type Type => TypeCore;
 
        internal abstract Type TypeCore { get; }
 
        internal abstract Type PublicType { get; }
 
        /// <summary>
        /// Returns the node type of this <see cref="Expression"/>. (Inherited from <see cref="Expression"/>.)
        /// </summary>
        /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
        public sealed override ExpressionType NodeType => ExpressionType.Lambda;
 
        /// <summary>
        /// Gets the parameters of the lambda expression.
        /// </summary>
        public ReadOnlyCollection<ParameterExpression> Parameters => GetOrMakeParameters();
 
        /// <summary>
        /// Gets the name of the lambda expression.
        /// </summary>
        /// <remarks>Used for debugging purposes.</remarks>
        public string? Name => NameCore;
 
        internal virtual string? NameCore => null;
 
        /// <summary>
        /// Gets the body of the lambda expression.
        /// </summary>
        public Expression Body => _body;
 
        /// <summary>
        /// Gets the return type of the lambda expression.
        /// </summary>
        public Type ReturnType => Type.GetInvokeMethod().ReturnType;
 
        /// <summary>
        /// Gets the value that indicates if the lambda expression will be compiled with
        /// tail call optimization.
        /// </summary>
        public bool TailCall => TailCallCore;
 
        internal virtual bool TailCallCore => false;
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual ReadOnlyCollection<ParameterExpression> GetOrMakeParameters()
        {
            throw ContractUtils.Unreachable;
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        ParameterExpression IParameterProvider.GetParameter(int index) => GetParameter(index);
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual ParameterExpression GetParameter(int index)
        {
            throw ContractUtils.Unreachable;
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        int IParameterProvider.ParameterCount => ParameterCount;
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual int ParameterCount
        {
            get
            {
                throw ContractUtils.Unreachable;
            }
        }
 
        /// <summary>
        /// Gets the Compile() MethodInfo on the specified LambdaExpression type.
        /// </summary>
        /// <remarks>
        /// Note that Expression{TDelegate} defines a 'new' Compile() method that hides the base
        /// LambdaExpression.Compile() method.
        /// </remarks>
        internal static MethodInfo GetCompileMethod(Type lambdaExpressionType)
        {
            Debug.Assert(lambdaExpressionType.IsAssignableTo(typeof(LambdaExpression)));
 
            if (lambdaExpressionType == typeof(LambdaExpression))
            {
                // use a hard-coded type directly so the method doesn't get trimmed
                return typeof(LambdaExpression).GetMethod("Compile", Type.EmptyTypes)!;
            }
 
            return (MethodInfo)lambdaExpressionType.GetMemberWithSameMetadataDefinitionAs(s_expressionCompileMethodInfo);
        }
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public Delegate Compile()
        {
            if (CanCompileToIL)
            {
                return Compiler.LambdaCompiler.Compile(this);
            }
            else
            {
                Debug.Assert(CanInterpret);
                return new Interpreter.LightCompiler().CompileTop(this).CreateDelegate();
            }
        }
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <param name="preferInterpretation">A <see cref="bool"/> that indicates if the expression should be compiled to an interpreted form, if available.</param>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public Delegate Compile(bool preferInterpretation)
        {
            if (CanInterpret && preferInterpretation)
            {
                return new Interpreter.LightCompiler().CompileTop(this).CreateDelegate();
            }
 
            return Compile();
        }
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        /// <summary>
        /// Compiles the lambda into a method definition.
        /// </summary>
        /// <param name="method">A <see cref="Emit.MethodBuilder"/> which will be used to hold the lambda's IL.</param>
        public void CompileToMethod(System.Reflection.Emit.MethodBuilder method)
        {
            ArgumentNullException.ThrowIfNull(method);
            ContractUtils.Requires(method.IsStatic, nameof(method));
            var type = method.DeclaringType as System.Reflection.Emit.TypeBuilder;
            if (type == null) throw Error.MethodBuilderDoesNotHaveTypeBuilder();
 
            Compiler.LambdaCompiler.Compile(this, method);
        }
#endif
 
        internal abstract LambdaExpression Accept(Compiler.StackSpiller spiller);
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <param name="debugInfoGenerator">Debugging information generator used by the compiler to mark sequence points and annotate local variables.</param>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public Delegate Compile(DebugInfoGenerator debugInfoGenerator)
        {
            return Compile();
        }
    }
 
    /// <summary>
    /// Defines a <see cref="Expression{TDelegate}"/> node.
    /// This captures a block of code that is similar to a .NET method body.
    /// </summary>
    /// <typeparam name="TDelegate">The type of the delegate.</typeparam>
    /// <remarks>
    /// Lambda expressions take input through parameters and are expected to be fully bound.
    /// </remarks>
    public class Expression<TDelegate> : LambdaExpression
    {
        internal Expression(Expression body)
            : base(body)
        {
        }
 
        internal sealed override Type TypeCore => typeof(TDelegate);
 
        internal override Type PublicType => typeof(Expression<TDelegate>);
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public new TDelegate Compile()
        {
            if (CanCompileToIL)
            {
                return (TDelegate)(object)Compiler.LambdaCompiler.Compile(this);
            }
            else
            {
                Debug.Assert(CanInterpret);
                return (TDelegate)(object)new Interpreter.LightCompiler().CompileTop(this).CreateDelegate();
            }
        }
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <param name="preferInterpretation">A <see cref="bool"/> that indicates if the expression should be compiled to an interpreted form, if available.</param>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public new TDelegate Compile(bool preferInterpretation)
        {
            if (CanInterpret && preferInterpretation)
            {
                return (TDelegate)(object)new Interpreter.LightCompiler().CompileTop(this).CreateDelegate();
            }
 
            return Compile();
        }
 
        /// <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="body">The <see cref="LambdaExpression.Body" /> property of the result.</param>
        /// <param name="parameters">The <see cref="LambdaExpression.Parameters" /> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public Expression<TDelegate> Update(Expression body, IEnumerable<ParameterExpression>? parameters)
        {
            if (body == Body)
            {
                // Ensure parameters is safe to enumerate twice.
                // (If this means a second call to ToReadOnly it will return quickly).
                ICollection<ParameterExpression>? pars;
                if (parameters == null)
                {
                    pars = null;
                }
                else
                {
                    pars = parameters as ICollection<ParameterExpression>;
                    if (pars == null)
                    {
                        parameters = pars = parameters.ToReadOnly();
                    }
                }
 
                if (SameParameters(pars))
                {
                    return this;
                }
            }
 
            return Lambda<TDelegate>(body, Name, TailCall, parameters);
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual bool SameParameters(ICollection<ParameterExpression>? parameters)
        {
            throw ContractUtils.Unreachable;
        }
 
        [ExcludeFromCodeCoverage(Justification = "Unreachable")]
        internal virtual Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            throw ContractUtils.Unreachable;
        }
 
        /// <summary>
        /// Dispatches to the specific visit method for this node type.
        /// </summary>
        protected internal override Expression Accept(ExpressionVisitor visitor)
        {
            return visitor.VisitLambda(this);
        }
 
        internal override LambdaExpression Accept(Compiler.StackSpiller spiller)
        {
            return spiller.Rewrite(this);
        }
 
        internal static Expression<TDelegate> Create(Expression body, string? name, bool tailCall, IReadOnlyList<ParameterExpression> parameters)
        {
            if (name == null && !tailCall)
            {
                return parameters.Count switch
                {
                    0 => new Expression0<TDelegate>(body),
                    1 => new Expression1<TDelegate>(body, parameters[0]),
                    2 => new Expression2<TDelegate>(body, parameters[0], parameters[1]),
                    3 => new Expression3<TDelegate>(body, parameters[0], parameters[1], parameters[2]),
                    _ => new ExpressionN<TDelegate>(body, parameters),
                };
            }
 
            return new FullExpression<TDelegate>(body, name, tailCall, parameters);
        }
 
        /// <summary>
        /// Produces a delegate that represents the lambda expression.
        /// </summary>
        /// <param name="debugInfoGenerator">Debugging information generator used by the compiler to mark sequence points and annotate local variables.</param>
        /// <returns>A delegate containing the compiled version of the lambda.</returns>
        public new TDelegate Compile(DebugInfoGenerator debugInfoGenerator)
        {
            return Compile();
        }
    }
 
    // Separate expression creation class to hide the CreateExpressionFunc function from users reflecting on Expression<T>
    internal static class ExpressionCreator<TDelegate>
    {
        public static Expression<TDelegate> CreateExpressionFunc(Expression body, string? name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters)
        {
            if (name == null && !tailCall)
            {
                switch (parameters.Count)
                {
                    case 0: return new Expression0<TDelegate>(body);
                    case 1: return new Expression1<TDelegate>(body, parameters[0]);
                    case 2: return new Expression2<TDelegate>(body, parameters[0], parameters[1]);
                    case 3: return new Expression3<TDelegate>(body, parameters[0], parameters[1], parameters[2]);
                    default: return new ExpressionN<TDelegate>(body, parameters);
                }
            }
 
            return new FullExpression<TDelegate>(body, name, tailCall, parameters);
        }
    }
 
    internal sealed class Expression0<TDelegate> : Expression<TDelegate>
    {
        public Expression0(Expression body)
            : base(body)
        {
        }
 
        internal override int ParameterCount => 0;
 
        internal override bool SameParameters(ICollection<ParameterExpression>? parameters) =>
            parameters == null || parameters.Count == 0;
 
        internal override ParameterExpression GetParameter(int index)
        {
            throw Error.ArgumentOutOfRange(nameof(index));
        }
 
        internal override ReadOnlyCollection<ParameterExpression> GetOrMakeParameters() => ReadOnlyCollection<ParameterExpression>.Empty;
 
        internal override Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            Debug.Assert(body != null);
            Debug.Assert(parameters == null || parameters.Length == 0);
 
            return Expression.Lambda<TDelegate>(body, parameters);
        }
    }
 
    internal sealed class Expression1<TDelegate> : Expression<TDelegate>
    {
        private object _par0;
 
        public Expression1(Expression body, ParameterExpression par0)
            : base(body)
        {
            _par0 = par0;
        }
 
        internal override int ParameterCount => 1;
 
        internal override ParameterExpression GetParameter(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<ParameterExpression>(_par0),
                _ => throw Error.ArgumentOutOfRange(nameof(index)),
            };
 
        internal override bool SameParameters(ICollection<ParameterExpression>? parameters)
        {
            if (parameters != null && parameters.Count == 1)
            {
                using (IEnumerator<ParameterExpression> en = parameters.GetEnumerator())
                {
                    en.MoveNext();
                    return en.Current == ExpressionUtils.ReturnObject<ParameterExpression>(_par0);
                }
            }
 
            return false;
        }
 
        internal override ReadOnlyCollection<ParameterExpression> GetOrMakeParameters() => ExpressionUtils.ReturnReadOnly(this, ref _par0);
 
        internal override Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            Debug.Assert(body != null);
            Debug.Assert(parameters == null || parameters.Length == 1);
 
            if (parameters != null)
            {
                return Expression.Lambda<TDelegate>(body, parameters);
            }
 
            return Expression.Lambda<TDelegate>(body, ExpressionUtils.ReturnObject<ParameterExpression>(_par0));
        }
    }
 
    internal sealed class Expression2<TDelegate> : Expression<TDelegate>
    {
        private object _par0;
        private readonly ParameterExpression _par1;
 
        public Expression2(Expression body, ParameterExpression par0, ParameterExpression par1)
            : base(body)
        {
            _par0 = par0;
            _par1 = par1;
        }
 
        internal override int ParameterCount => 2;
 
        internal override ParameterExpression GetParameter(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<ParameterExpression>(_par0),
                1 => _par1,
                _ => throw Error.ArgumentOutOfRange(nameof(index)),
            };
 
        internal override bool SameParameters(ICollection<ParameterExpression>? parameters)
        {
            if (parameters != null && parameters.Count == 2)
            {
                if (_par0 is ReadOnlyCollection<ParameterExpression> alreadyCollection)
                {
                    return ExpressionUtils.SameElements(parameters, alreadyCollection);
                }
 
                using (IEnumerator<ParameterExpression> en = parameters.GetEnumerator())
                {
                    en.MoveNext();
                    if (en.Current == _par0)
                    {
                        en.MoveNext();
                        return en.Current == _par1;
                    }
                }
            }
 
            return false;
        }
 
 
        internal override ReadOnlyCollection<ParameterExpression> GetOrMakeParameters() => ExpressionUtils.ReturnReadOnly(this, ref _par0);
 
        internal override Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            Debug.Assert(body != null);
            Debug.Assert(parameters == null || parameters.Length == 2);
 
            if (parameters != null)
            {
                return Expression.Lambda<TDelegate>(body, parameters);
            }
 
            return Expression.Lambda<TDelegate>(body, ExpressionUtils.ReturnObject<ParameterExpression>(_par0), _par1);
        }
    }
 
    internal sealed class Expression3<TDelegate> : Expression<TDelegate>
    {
        private object _par0;
        private readonly ParameterExpression _par1;
        private readonly ParameterExpression _par2;
 
        public Expression3(Expression body, ParameterExpression par0, ParameterExpression par1, ParameterExpression par2)
            : base(body)
        {
            _par0 = par0;
            _par1 = par1;
            _par2 = par2;
        }
 
        internal override int ParameterCount => 3;
 
        internal override ParameterExpression GetParameter(int index) =>
            index switch
            {
                0 => ExpressionUtils.ReturnObject<ParameterExpression>(_par0),
                1 => _par1,
                2 => _par2,
                _ => throw Error.ArgumentOutOfRange(nameof(index)),
            };
 
        internal override bool SameParameters(ICollection<ParameterExpression>? parameters)
        {
            if (parameters != null && parameters.Count == 3)
            {
                if (_par0 is ReadOnlyCollection<ParameterExpression> alreadyCollection)
                {
                    return ExpressionUtils.SameElements(parameters, alreadyCollection);
                }
 
                using (IEnumerator<ParameterExpression> en = parameters.GetEnumerator())
                {
                    en.MoveNext();
                    if (en.Current == _par0)
                    {
                        en.MoveNext();
                        if (en.Current == _par1)
                        {
                            en.MoveNext();
                            return en.Current == _par2;
                        }
                    }
                }
            }
 
            return false;
        }
 
        internal override ReadOnlyCollection<ParameterExpression> GetOrMakeParameters() => ExpressionUtils.ReturnReadOnly(this, ref _par0);
 
        internal override Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            Debug.Assert(body != null);
            Debug.Assert(parameters == null || parameters.Length == 3);
 
            if (parameters != null)
            {
                return Expression.Lambda<TDelegate>(body, parameters);
            }
 
            return Expression.Lambda<TDelegate>(body, ExpressionUtils.ReturnObject<ParameterExpression>(_par0), _par1, _par2);
        }
    }
 
    internal class ExpressionN<TDelegate> : Expression<TDelegate>
    {
        private IReadOnlyList<ParameterExpression> _parameters;
 
        public ExpressionN(Expression body, IReadOnlyList<ParameterExpression> parameters)
            : base(body)
        {
            _parameters = parameters;
        }
 
        internal override int ParameterCount => _parameters.Count;
 
        internal override ParameterExpression GetParameter(int index) => _parameters[index];
 
        internal override bool SameParameters(ICollection<ParameterExpression>? parameters) =>
            ExpressionUtils.SameElements(parameters, _parameters);
 
        internal override ReadOnlyCollection<ParameterExpression> GetOrMakeParameters() => ExpressionUtils.ReturnReadOnly(ref _parameters);
 
        internal override Expression<TDelegate> Rewrite(Expression body, ParameterExpression[]? parameters)
        {
            Debug.Assert(body != null);
            Debug.Assert(parameters == null || parameters.Length == _parameters.Count);
 
            return Expression.Lambda<TDelegate>(body, Name, TailCall, parameters ?? _parameters);
        }
    }
 
    internal sealed class FullExpression<TDelegate> : ExpressionN<TDelegate>
    {
        public FullExpression(Expression body, string? name, bool tailCall, IReadOnlyList<ParameterExpression> parameters)
            : base(body, parameters)
        {
            NameCore = name;
            TailCallCore = tailCall;
        }
 
        internal override string? NameCore { get; }
        internal override bool TailCallCore { get; }
    }
 
    public partial class Expression
    {
        /// <summary>
        /// Creates an Expression{T} given the delegate type. Caches the
        /// factory method to speed up repeated creations for the same T.
        /// </summary>
        [UnconditionalSuppressMessage("DynamicCode", "IL3050",
            Justification = "MakeGenericType is only used for a Type that should be a delegate type, which are always reference types.")]
        internal static LambdaExpression CreateLambda(Type delegateType, Expression body, string? name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters)
        {
            // Get or create a delegate to the public Expression.Lambda<T>
            // method and call that will be used for creating instances of this
            // delegate type
            Func<Expression, string?, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>? fastPath;
            CacheDict<Type, Func<Expression, string?, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>>? factories =
                s_lambdaFactories ??= new CacheDict<Type, Func<Expression, string?, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>>(50);
 
            if (!factories.TryGetValue(delegateType, out fastPath))
            {
                MethodInfo create;
                if (LambdaExpression.CanCompileToIL)
                {
                    create = typeof(Expression<>).MakeGenericType(delegateType).GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic)!;
                }
                else
                {
                    create = typeof(ExpressionCreator<>).MakeGenericType(delegateType).GetMethod("CreateExpressionFunc", BindingFlags.Static | BindingFlags.Public)!;
                }
 
                if (delegateType.IsCollectible)
                {
                    return (LambdaExpression)create.Invoke(null, new object?[] { body, name, tailCall, parameters })!;
                }
 
                factories[delegateType] = fastPath = (Func<Expression, string?, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>)create.CreateDelegate(typeof(Func<Expression, string?, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>));
            }
 
            return fastPath(body, name, tailCall, parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[]? parameters)
        {
            return Lambda<TDelegate>(body, false, (IEnumerable<ParameterExpression>?)parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, bool tailCall, params ParameterExpression[]? parameters)
        {
            return Lambda<TDelegate>(body, tailCall, (IEnumerable<ParameterExpression>?)parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda<TDelegate>(body, null, false, parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda<TDelegate>(body, null, tailCall, parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="name">The name of the lambda. Used for generating debugging info.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, string? name, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda<TDelegate>(body, name, false, parameters);
        }
 
        /// <summary>
        /// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type.</typeparam>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="name">The name of the lambda. Used for generating debugging info.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static Expression<TDelegate> Lambda<TDelegate>(Expression body, string? name, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            ReadOnlyCollection<ParameterExpression> parameterList = parameters.ToReadOnly();
            ValidateLambdaArgs(typeof(TDelegate), ref body, parameterList, nameof(TDelegate));
            if (LambdaExpression.CanCompileToIL)
            {
                return Expression<TDelegate>.Create(body, name, tailCall, parameterList);
            }
            else
            {
                return ExpressionCreator<TDelegate>.CreateExpressionFunc(body, name, tailCall, parameterList);
            }
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, params ParameterExpression[]? parameters)
        {
            return Lambda(body, false, (IEnumerable<ParameterExpression>?)parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, bool tailCall, params ParameterExpression[]? parameters)
        {
            return Lambda(body, tailCall, (IEnumerable<ParameterExpression>?)parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda(body, null, false, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda(body, null, tailCall, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[]? parameters)
        {
            return Lambda(delegateType, body, null, false, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, bool tailCall, params ParameterExpression[]? parameters)
        {
            return Lambda(delegateType, body, null, tailCall, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda(delegateType, body, null, false, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda(delegateType, body, null, tailCall, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="name">The name for the lambda. Used for emitting debug information.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, string? name, IEnumerable<ParameterExpression>? parameters)
        {
            return Lambda(body, name, false, parameters);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="name">The name for the lambda. Used for emitting debug information.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static LambdaExpression Lambda(Expression body, string? name, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            ArgumentNullException.ThrowIfNull(body);
 
            ReadOnlyCollection<ParameterExpression> parameterList = parameters.ToReadOnly();
 
            int paramCount = parameterList.Count;
            Type[] typeArgs = new Type[paramCount + 1];
            if (paramCount > 0)
            {
                var set = new HashSet<ParameterExpression>();
                for (int i = 0; i < paramCount; i++)
                {
                    ParameterExpression param = parameterList[i];
                    ArgumentNullException.ThrowIfNull(param, "parameter");
                    typeArgs[i] = param.IsByRef ? param.Type.MakeByRefType() : param.Type;
                    if (!set.Add(param))
                    {
                        throw Error.DuplicateVariable(param, nameof(parameters), i);
                    }
                }
            }
            typeArgs[paramCount] = body.Type;
 
            Type delegateType = Compiler.DelegateHelpers.MakeDelegateType(typeArgs);
 
            return CreateLambda(delegateType, body, name, tailCall, parameterList);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <param name="name">The name for the lambda. Used for emitting debug information.</param>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, string? name, IEnumerable<ParameterExpression>? parameters)
        {
            ReadOnlyCollection<ParameterExpression> paramList = parameters.ToReadOnly();
            ValidateLambdaArgs(delegateType, ref body, paramList, nameof(delegateType));
 
            return CreateLambda(delegateType, body, name, false, paramList);
        }
 
        /// <summary>
        /// Creates a <see cref="LambdaExpression"/> by first constructing a delegate type.
        /// </summary>
        /// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
        /// <param name="body">An <see cref="Expression"/> to set the <see cref="LambdaExpression.Body"/> property equal to.</param>
        /// <param name="name">The name for the lambda. Used for emitting debug information.</param>
        /// <param name="tailCall">A <see cref="bool"/> that indicates if tail call optimization will be applied when compiling the created expression.</param>
        /// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="LambdaExpression.Parameters"/> collection.</param>
        /// <returns>A <see cref="LambdaExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.Lambda"/> and the <see cref="LambdaExpression.Body"/> and <see cref="LambdaExpression.Parameters"/> properties set to the specified values.</returns>
        public static LambdaExpression Lambda(Type delegateType, Expression body, string? name, bool tailCall, IEnumerable<ParameterExpression>? parameters)
        {
            ReadOnlyCollection<ParameterExpression> paramList = parameters.ToReadOnly();
            ValidateLambdaArgs(delegateType, ref body, paramList, nameof(delegateType));
 
            return CreateLambda(delegateType, body, name, tailCall, paramList);
        }
 
        private static void ValidateLambdaArgs(Type delegateType, ref Expression body, ReadOnlyCollection<ParameterExpression> parameters, string paramName)
        {
            ArgumentNullException.ThrowIfNull(delegateType);
            ExpressionUtils.RequiresCanRead(body, nameof(body));
 
            if (!typeof(MulticastDelegate).IsAssignableFrom(delegateType) || delegateType == typeof(MulticastDelegate))
            {
                throw Error.LambdaTypeMustBeDerivedFromSystemDelegate(paramName);
            }
 
            TypeUtils.ValidateType(delegateType, nameof(delegateType), allowByRef: true, allowPointer: true);
 
            CacheDict<Type, MethodInfo> ldc = s_lambdaDelegateCache;
            if (!ldc.TryGetValue(delegateType, out MethodInfo? mi))
            {
                mi = delegateType.GetInvokeMethod();
                if (!delegateType.IsCollectible)
                {
                    ldc[delegateType] = mi;
                }
            }
 
            ParameterInfo[] pis = mi.GetParametersCached();
 
            if (pis.Length > 0)
            {
                if (pis.Length != parameters.Count)
                {
                    throw Error.IncorrectNumberOfLambdaDeclarationParameters();
                }
                var set = new HashSet<ParameterExpression>();
                for (int i = 0, n = pis.Length; i < n; i++)
                {
                    ParameterExpression pex = parameters[i];
                    ParameterInfo pi = pis[i];
                    ExpressionUtils.RequiresCanRead(pex, nameof(parameters), i);
                    Type pType = pi.ParameterType;
                    if (pex.IsByRef)
                    {
                        if (!pType.IsByRef)
                        {
                            //We cannot pass a parameter of T& to a delegate that takes T or any non-ByRef type.
                            throw Error.ParameterExpressionNotValidAsDelegate(pex.Type.MakeByRefType(), pType);
                        }
                        pType = pType.GetElementType()!;
                    }
                    if (!TypeUtils.AreReferenceAssignable(pex.Type, pType))
                    {
                        throw Error.ParameterExpressionNotValidAsDelegate(pex.Type, pType);
                    }
                    if (!set.Add(pex))
                    {
                        throw Error.DuplicateVariable(pex, nameof(parameters), i);
                    }
                }
            }
            else if (parameters.Count > 0)
            {
                throw Error.IncorrectNumberOfLambdaDeclarationParameters();
            }
            if (mi.ReturnType != typeof(void) && !TypeUtils.AreReferenceAssignable(mi.ReturnType, body.Type))
            {
                if (!TryQuote(mi.ReturnType, ref body))
                {
                    throw Error.ExpressionTypeDoesNotMatchReturn(body.Type, mi.ReturnType);
                }
            }
        }
 
        private enum TryGetFuncActionArgsResult
        {
            Valid,
            ArgumentNull,
            ByRef,
            PointerOrVoid
        }
 
        private static TryGetFuncActionArgsResult ValidateTryGetFuncActionArgs(Type[]? typeArgs)
        {
            if (typeArgs == null)
            {
                return TryGetFuncActionArgsResult.ArgumentNull;
            }
 
            for (int i = 0; i < typeArgs.Length; i++)
            {
                Type a = typeArgs[i];
                if (a == null)
                {
                    return TryGetFuncActionArgsResult.ArgumentNull;
                }
 
                if (a.IsByRef)
                {
                    return TryGetFuncActionArgsResult.ByRef;
                }
 
                if (a == typeof(void) || a.IsPointer)
                {
                    return TryGetFuncActionArgsResult.PointerOrVoid;
                }
            }
 
            return TryGetFuncActionArgsResult.Valid;
        }
 
        /// <summary>
        /// Creates a <see cref="System.Type"/> object that represents a generic System.Func delegate type that has specific type arguments.
        /// The last type argument specifies the return type of the created delegate.
        /// </summary>
        /// <param name="typeArgs">An array of <see cref="System.Type"/> objects that specify the type arguments for the System.Func delegate type.</param>
        /// <returns>The type of a System.Func delegate that has the specified type arguments.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static Type GetFuncType(params Type[]? typeArgs)
        {
            switch (ValidateTryGetFuncActionArgs(typeArgs))
            {
                case TryGetFuncActionArgsResult.ArgumentNull:
                    throw new ArgumentNullException(nameof(typeArgs));
                case TryGetFuncActionArgsResult.ByRef:
                    throw Error.TypeMustNotBeByRef(nameof(typeArgs));
                default:
 
                    // This includes pointers or void. We allow the exception that comes
                    // from trying to use them as generic arguments to pass through.
                    Type result = Compiler.DelegateHelpers.GetFuncType(typeArgs);
                    if (result == null)
                    {
                        throw Error.IncorrectNumberOfTypeArgsForFunc(nameof(typeArgs));
                    }
 
                    return result;
            }
        }
 
        /// <summary>
        /// Creates a <see cref="System.Type"/> object that represents a generic System.Func delegate type that has specific type arguments.
        /// The last type argument specifies the return type of the created delegate.
        /// </summary>
        /// <param name="typeArgs">An array of <see cref="System.Type"/> objects that specify the type arguments for the System.Func delegate type.</param>
        /// <param name="funcType">When this method returns, contains the generic System.Func delegate type that has specific type arguments. Contains null if there is no generic System.Func delegate that matches the <paramref name="typeArgs"/>.This parameter is passed uninitialized.</param>
        /// <returns>true if generic System.Func delegate type was created for specific <paramref name="typeArgs"/>; false otherwise.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static bool TryGetFuncType(Type[] typeArgs, [NotNullWhen(true)] out Type? funcType)
        {
            if (ValidateTryGetFuncActionArgs(typeArgs) == TryGetFuncActionArgsResult.Valid)
            {
                return (funcType = Compiler.DelegateHelpers.GetFuncType(typeArgs)) != null;
            }
 
            funcType = null;
            return false;
        }
 
        /// <summary>
        /// Creates a <see cref="System.Type"/> object that represents a generic System.Action delegate type that has specific type arguments.
        /// </summary>
        /// <param name="typeArgs">An array of <see cref="System.Type"/> objects that specify the type arguments for the System.Action delegate type.</param>
        /// <returns>The type of a System.Action delegate that has the specified type arguments.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static Type GetActionType(params Type[]? typeArgs)
        {
            switch (ValidateTryGetFuncActionArgs(typeArgs))
            {
                case TryGetFuncActionArgsResult.ArgumentNull:
                    throw new ArgumentNullException(nameof(typeArgs));
                case TryGetFuncActionArgsResult.ByRef:
                    throw Error.TypeMustNotBeByRef(nameof(typeArgs));
                default:
 
                    // This includes pointers or void. We allow the exception that comes
                    // from trying to use them as generic arguments to pass through.
                    Type result = Compiler.DelegateHelpers.GetActionType(typeArgs);
                    if (result == null)
                    {
                        throw Error.IncorrectNumberOfTypeArgsForAction(nameof(typeArgs));
                    }
 
                    return result;
            }
        }
 
        /// <summary>
        /// Creates a <see cref="System.Type"/> object that represents a generic System.Action delegate type that has specific type arguments.
        /// </summary>
        /// <param name="typeArgs">An array of <see cref="System.Type"/> objects that specify the type arguments for the System.Action delegate type.</param>
        /// <param name="actionType">When this method returns, contains the generic System.Action delegate type that has specific type arguments. Contains null if there is no generic System.Action delegate that matches the <paramref name="typeArgs"/>.This parameter is passed uninitialized.</param>
        /// <returns>true if generic System.Action delegate type was created for specific <paramref name="typeArgs"/>; false otherwise.</returns>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static bool TryGetActionType(Type[] typeArgs, [NotNullWhen(true)] out Type? actionType)
        {
            if (ValidateTryGetFuncActionArgs(typeArgs) == TryGetFuncActionArgsResult.Valid)
            {
                return (actionType = Compiler.DelegateHelpers.GetActionType(typeArgs)) != null;
            }
 
            actionType = null;
            return false;
        }
 
        /// <summary>
        /// Gets a <see cref="System.Type"/> object that represents a generic System.Func or System.Action delegate type that has specific type arguments.
        /// The last type argument determines the return type of the delegate. If no Func or Action is large enough, it will generate a custom
        /// delegate type.
        /// </summary>
        /// <param name="typeArgs">An array of <see cref="System.Type"/> objects that specify the type arguments of the delegate type.</param>
        /// <returns>The delegate type.</returns>
        /// <remarks>
        /// As with Func, the last argument is the return type. It can be set
        /// to <see cref="System.Void"/> to produce an Action.</remarks>
        [RequiresDynamicCode(Expression.DelegateCreationRequiresDynamicCode)]
        public static Type GetDelegateType(params Type[] typeArgs)
        {
            ContractUtils.RequiresNotEmpty(typeArgs, nameof(typeArgs));
            ContractUtils.RequiresNotNullItems(typeArgs, nameof(typeArgs));
            return Compiler.DelegateHelpers.MakeDelegateType(typeArgs);
        }
    }
}