using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.ExceptionServices; using DynamicExpresso.Reflection; using DynamicExpresso.Resources; namespace DynamicExpresso { /// /// Represents a lambda expression that can be invoked. This class is thread safe. /// public class Lambda { private readonly Expression _expression; private readonly ParserArguments _parserArguments; private readonly Lazy _delegate; internal Lambda(Expression expression, ParserArguments parserArguments) { _expression = expression ?? throw new ArgumentNullException(nameof(expression)); _parserArguments = parserArguments ?? throw new ArgumentNullException(nameof(parserArguments)); // Note: I always lazy compile the generic lambda. Maybe in the future this can be a setting because if I generate a typed delegate this compilation is not required. _delegate = new Lazy(() => Expression.Lambda(_expression, _parserArguments.UsedParameters.Select(p => p.Expression).ToArray()).Compile()); } public Expression Expression { get { return _expression; } } public bool CaseInsensitive { get { return _parserArguments.Settings.CaseInsensitive; } } public string ExpressionText { get { return _parserArguments.ExpressionText; } } public Type ReturnType { get { return Expression.Type; } } /// /// Gets the parameters actually used in the expression parsed. /// /// The used parameters. [Obsolete("Use UsedParameters or DeclaredParameters")] public IEnumerable Parameters { get { return _parserArguments.UsedParameters; } } /// /// Gets the parameters actually used in the expression parsed. /// /// The used parameters. public IEnumerable UsedParameters { get { return _parserArguments.UsedParameters; } } /// /// Gets the parameters declared when parsing the expression. /// /// The declared parameters. public IEnumerable DeclaredParameters { get { return _parserArguments.DeclaredParameters; } } public IEnumerable Types { get { return _parserArguments.UsedTypes; } } public IEnumerable Identifiers { get { return _parserArguments.UsedIdentifiers; } } public object Invoke() { return InvokeWithUsedParameters(new object[0]); } public object Invoke(params Parameter[] parameters) { return Invoke((IEnumerable)parameters); } public object Invoke(IEnumerable parameters) { var args = (from usedParameter in UsedParameters from actualParameter in parameters where usedParameter.Name.Equals(actualParameter.Name, _parserArguments.Settings.KeyComparison) select actualParameter.Value) .ToArray(); return InvokeWithUsedParameters(args); } /// /// Invoke the expression with the given parameters values. /// /// Order of parameters must be the same of the parameters used during parse (DeclaredParameters). /// public object Invoke(params object[] args) { var parameters = new List(); var declaredParameters = DeclaredParameters.ToArray(); if (args != null) { if (declaredParameters.Length != args.Length) throw new InvalidOperationException(ErrorMessages.ArgumentCountMismatch); for (var i = 0; i < args.Length; i++) { var parameter = new Parameter( declaredParameters[i].Name, declaredParameters[i].Type, args[i]); parameters.Add(parameter); } } return Invoke(parameters); } private object InvokeWithUsedParameters(object[] orderedArgs) { try { return _delegate.Value.DynamicInvoke(orderedArgs); } catch (TargetInvocationException exc) { if (exc.InnerException != null) ExceptionDispatchInfo.Capture(exc.InnerException).Throw(); throw; } } public override string ToString() { return ExpressionText; } /// /// Generate the given delegate by compiling the lambda expression. /// /// The delegate to generate. Delegate parameters must match the one defined when creating the expression, see UsedParameters. public TDelegate Compile() { var lambdaExpression = LambdaExpression(); return lambdaExpression.Compile(); } [Obsolete("Use Compile()")] public TDelegate Compile(IEnumerable parameters) { var lambdaExpression = Expression.Lambda(_expression, parameters.Select(p => p.Expression).ToArray()); return lambdaExpression.Compile(); } /// /// Generate a lambda expression. /// /// The lambda expression. /// The delegate to generate. Delegate parameters must match the one defined when creating the expression, see UsedParameters. public Expression LambdaExpression() { return Expression.Lambda(_expression, DeclaredParameters.Select(p => p.Expression).ToArray()); } internal LambdaExpression LambdaExpression(Type delegateType) { var parameterExpressions = DeclaredParameters.Select(p => p.Expression).ToArray(); var types = delegateType.GetGenericArguments(); // return type if (delegateType.GetGenericTypeDefinition() == ReflectionExtensions.GetFuncType(parameterExpressions.Length)) types[types.Length - 1] = _expression.Type; var genericType = delegateType.GetGenericTypeDefinition(); var inferredDelegateType = genericType.MakeGenericType(types); return Expression.Lambda(inferredDelegateType, _expression, parameterExpressions); } } }