-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from Miaplaza/fix-expression-caching
Fix expression caching
- Loading branch information
Showing
9 changed files
with
165 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
|
||
namespace MiaPlaza.ExpressionUtils { | ||
/// <summary> | ||
/// Replaces all parameters in an lambda-expression by other expressions, i.e., given a lambda | ||
/// <c>(t0, …, tn) → f(t0,…,tn)</c> and expressions <c>(x0,…,xn)</c>, it returns the expression <c>f(x0,…,xn)</c>. | ||
/// </summary> | ||
/// <remarks> | ||
/// Does nothing else than that, esepicially does not change the structure of the expression or removes closures.false | ||
/// If you want that and other optimizations, use see <cref="ParameterSubstituter" /> | ||
/// </remarks> | ||
public class SimpleParameterSubstituter : ExpressionVisitor { | ||
public static Expression SubstituteParameter(LambdaExpression expression, params Expression[] replacements) | ||
=> SubstituteParameter(expression, replacements as IReadOnlyCollection<Expression>); | ||
|
||
public static Expression SubstituteParameter(LambdaExpression expression, IEnumerable<Expression> replacements) | ||
=> SubstituteParameter(expression, replacements.ToList()); | ||
|
||
public static Expression SubstituteParameter(LambdaExpression expression, IReadOnlyCollection<Expression> replacements) { | ||
if (expression == null) { | ||
throw new ArgumentNullException(nameof(expression)); | ||
} | ||
|
||
if (replacements == null) { | ||
throw new ArgumentNullException(nameof(replacements)); | ||
} | ||
|
||
if (expression.Parameters.Count != replacements.Count) { | ||
throw new ArgumentException($"Replacement count does not match parameter count ({replacements.Count} vs {expression.Parameters.Count})"); | ||
} | ||
|
||
var dict = new Dictionary<ParameterExpression, Expression>(); | ||
|
||
foreach (var tuple in expression.Parameters.Zip(replacements, (p, r) => new { parameter = p, replacement = r })) { | ||
if (!tuple.parameter.Type.IsAssignableFrom(tuple.replacement.Type)) { | ||
throw new ArgumentException($"The expression {tuple.replacement} cannot be used as replacement for the parameter {tuple.parameter}."); | ||
} | ||
dict[tuple.parameter] = tuple.replacement; | ||
} | ||
|
||
return new SimpleParameterSubstituter(dict).Visit(expression.Body); | ||
} | ||
|
||
public static Expression SubstituteParameter(Expression expression, IReadOnlyDictionary<ParameterExpression, Expression> replacements) | ||
=> new SimpleParameterSubstituter(replacements).Visit(expression); | ||
|
||
readonly IReadOnlyDictionary<ParameterExpression, Expression> replacements; | ||
|
||
protected SimpleParameterSubstituter(IReadOnlyDictionary<ParameterExpression, Expression> replacements) { | ||
this.replacements = replacements; | ||
} | ||
|
||
protected override Expression VisitParameter(ParameterExpression node) { | ||
Expression replacement; | ||
if (replacements.TryGetValue(node, out replacement)) { | ||
return replacement; | ||
} else { | ||
return node; | ||
} | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
ExpressionUtilsTest/CachedExpressionCompilerTestEvaluator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System.Linq.Expressions; | ||
using MiaPlaza.ExpressionUtils; | ||
using MiaPlaza.ExpressionUtils.Evaluating; | ||
using NUnit.Framework; | ||
|
||
namespace MiaPlaza.Test.ExpressionUtilsTest { | ||
public class CachedExpressionCompilerTestEvaluator : IExpressionEvaluator { | ||
|
||
public object Evaluate(Expression unparametrizedExpression) { | ||
var lambda = Expression.Lambda(unparametrizedExpression); | ||
return this.EvaluateLambda(lambda)(); | ||
} | ||
|
||
public VariadicArrayParametersDelegate EvaluateLambda(LambdaExpression lambdaExpression) { | ||
var result = ((IExpressionEvaluator)CachedExpressionCompiler.Instance).EvaluateLambda(lambdaExpression); | ||
Assert.IsTrue(CachedExpressionCompiler.Instance.IsCached(lambdaExpression)); | ||
return result; | ||
} | ||
|
||
public DELEGATE EvaluateTypedLambda<DELEGATE>(Expression<DELEGATE> expression) where DELEGATE : class { | ||
|
||
var result = this.EvaluateLambda(expression); | ||
return result.WrapDelegate<DELEGATE>(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters