public void Add(NestedLambdaInfo nestedLambdaInfo)
 {
     if (NestedLambdas == null)
     {
         NestedLambdas = new List <NestedLambdaInfo>();
     }
     NestedLambdas.Add(nestedLambdaInfo);
 }
        // @paramExprs is required for nested lambda compilation
        private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression expr, IList <ParameterExpression> paramExprs)
        {
            if (expr == null)
            {
                return(false);
            }

            switch (expr.NodeType)
            {
            case ExpressionType.Constant:
                var constantExpr  = (ConstantExpression)expr;
                var constantValue = constantExpr.Value;

                if (constantValue is Delegate ||
                    IsBoundConstant(constantValue))
                {
                    closure = closure ?? new ClosureInfo();
                    closure.Add(constantExpr);
                }

                break;

            case ExpressionType.Parameter:
                // if parameter is used but no passed we assume that it should be in closure and set by outer lambda
                var paramExpr = (ParameterExpression)expr;
                if (paramExprs.IndexOf(paramExpr) == -1)
                {
                    closure = closure ?? new ClosureInfo();
                    closure.Add(paramExpr);
                }
                break;

            case ExpressionType.Call:
                var methodCallExpr  = (MethodCallExpression)expr;
                var methodOwnerExpr = methodCallExpr.Object;

                return((methodOwnerExpr == null ||
                        TryCollectBoundConstants(ref closure, methodOwnerExpr, paramExprs)) &&
                       TryCollectBoundConstants(ref closure, methodCallExpr.Arguments, paramExprs));

            case ExpressionType.MemberAccess:
                return(TryCollectBoundConstants(ref closure, ((MemberExpression)expr).Expression, paramExprs));

            case ExpressionType.New:
                return(TryCollectBoundConstants(ref closure, ((NewExpression)expr).Arguments, paramExprs));

            case ExpressionType.NewArrayInit:
                return(TryCollectBoundConstants(ref closure, ((NewArrayExpression)expr).Expressions,
                                                paramExprs));

            // property initializer
            case ExpressionType.MemberInit:
                var memberInitExpr = (MemberInitExpression)expr;
                if (!TryCollectBoundConstants(ref closure, memberInitExpr.NewExpression, paramExprs))
                {
                    return(false);
                }

                var memberBindings = memberInitExpr.Bindings;
                for (var i = 0; i < memberBindings.Count; ++i)
                {
                    var memberBinding = memberBindings[i];
                    if (memberBinding.BindingType == MemberBindingType.Assignment &&
                        !TryCollectBoundConstants(ref closure, ((MemberAssignment)memberBinding).Expression,
                                                  paramExprs))
                    {
                        return(false);
                    }
                }
                break;

            // nested lambda
            case ExpressionType.Lambda:

                var lambdaExpr       = (LambdaExpression)expr;
                var lambdaParamExprs = lambdaExpr.Parameters;
                var paramTypes       = GetParamExprTypes(lambdaParamExprs);

                ClosureInfo nestedClosure = null;
                var         nestedLambda  = TryCompile(ref nestedClosure,
                                                       lambdaExpr.Type, paramTypes, lambdaExpr.Body.Type, lambdaExpr.Body, lambdaParamExprs);

                if (nestedLambda == null)
                {
                    return(false);
                }

                var nestedLambdaInfo = new NestedLambdaInfo(nestedLambda, expr, nestedClosure);

                closure = closure ?? new ClosureInfo();
                closure.Add(nestedLambdaInfo);

                // if nested parameter is no matched with any outer parameter, that ensure it goes to outer closure
                if (nestedClosure != null && nestedClosure.UsedParamExpressions != null)
                {
                    var nestedClosedParams = nestedClosure.UsedParamExpressions;
                    for (var i = 0; i < nestedClosedParams.Count; i++)
                    {
                        var nestedClosedParamExpr = nestedClosedParams[i];
                        if (paramExprs.Count == 0 ||
                            paramExprs.IndexOf(nestedClosedParamExpr) == -1)
                        {
                            closure.Add(nestedClosedParamExpr);
                        }
                    }
                }

                break;

            case ExpressionType.Invoke:
                var invocationExpr = (InvocationExpression)expr;
                return(TryCollectBoundConstants(ref closure, invocationExpr.Expression, paramExprs) &&
                       TryCollectBoundConstants(ref closure, invocationExpr.Arguments, paramExprs));

            default:
                var unaryExpr = expr as UnaryExpression;
                if (unaryExpr != null)
                {
                    return(TryCollectBoundConstants(ref closure, unaryExpr.Operand, paramExprs));
                }

                var binaryExpr = expr as BinaryExpression;
                if (binaryExpr != null)
                {
                    return(TryCollectBoundConstants(ref closure, binaryExpr.Left, paramExprs) &&
                           TryCollectBoundConstants(ref closure, binaryExpr.Right, paramExprs));
                }

                break;
            }

            return(true);
        }