Skip to content

Tools allowing to parse textual C# Lambda Expression and more.

License

Notifications You must be signed in to change notification settings

xaviersolau/ExpressionTools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExpressionTools

ExpressionTools is a library that helps you to handle C# lambda expression. It is written in C# and thanks to .Net Standard, it is cross platform.

The project is based on System.Linq.Expressions and uses the C# Roslyn compiler.

Don't hesitate to post issues, pull requests on the project or to fork and improve the project.

Project dashboard

Build - CI Coverage Status License: MIT

Package Nuget.org Pre-release
SoloX.ExpressionTools.Parser NuGet Beta NuGet Beta
SoloX.ExpressionTools.Transform NuGet Beta NuGet Beta

License and credits

ExpressionTools project is written by Xavier Solau. It's licensed under the MIT license.


Installation

You can checkout this Github repository or you can use the NuGet package:

Install using the command line from the Package Manager:

Install-Package SoloX.ExpressionTools.Parser -version 1.0.4
Install-Package SoloX.ExpressionTools.Transform -version 1.0.4

Install using the .Net CLI:

dotnet add package SoloX.ExpressionTools.Parser --version 1.0.4
dotnet add package SoloX.ExpressionTools.Transform --version 1.0.4

Install editing your project file (csproj):

<PackageReference Include="SoloX.ExpressionTools.Parser" Version="1.0.4" />
<PackageReference Include="SoloX.ExpressionTools.Transform" Version="1.0.4" />

How to use it

Note that you can find code examples in this repository in this location: src/examples.

Parse a C# Lambda expression

Simple

A few lines of code are actually needed to parse a textual C# lambda expression. Let's say that we want to parse a simple lambda expression like "(int x) => x + 1" with x as an integer:

// Set the expression to parse
var expToParse = "(int x) => x + 1";

// We need to create the parser.
var expressionParser = new ExpressionParser();

// We can just parse the expression.
var lambdaExpression = expressionParser.Parse(expToParse);

// Or we can parse the expression specifying the type of the lambda expression.
var expression = expressionParser.Parse<Func<int, int>>(expToParse);

The Parse method will return an expression tree (from System.Linq.Expressions) reflecting the given textual C# lambda expression.

More precisely, The resulting expression object is based on System.Linq.Expressions.LambdaExpression. It means that you can then use the Compile method to compute a Delegate in order to call the lambda at full speed.

Using a IParameterTypeResolver

Unfortunately, things can get a little bit more complicated if you want to parse the same expression without specifying the parameter type giving us a expression like "x => x + 1". In this case you will need to provide to the ExpressionParser a IParameterTypeResolver that will resolve the type of the parameter x as an integer:

// Set the expression to parse
var expToParse = "x => x + 1";

// We need to create the parser with a DictionaryParameterTypeResolver that will resolve the
// parameter name using the given Dictionary.
var expressionParser = new ExpressionParser(
    parameterTypeResolver: new DictionaryParameterTypeResolver(new Dictionary<string, Type>()
    {
        { "x", typeof(int) },
    }));

// We can just parse the expression.
var expression = expressionParser.Parse<Func<int, int>>(expToParse);

Using a ITypeNameResolver

Let's now use a static method Max that is defined in System.Math with a full qualified name like "(double x, double y) => Math.Max(x, y)". To support this use case, you will need to provide a ITypeNameResolver that will resolve the Math as System.Math class:

// Set the expression to parse
var expToParse = "(double x, double y) => Math.Max(x, y)";

// We need to create the parser with a TypeNameResolver that will resolve type name with
// the given System.Math class.
var expressionParser = new ExpressionParser(
    typeNameResolver: new TypeNameResolver(typeof(Math)));

// We can just parse the expression.
var expression = expressionParser.Parse<Func<double, double, double>>(expToParse);

Using a IMethodResolver

Now we want our expression to use a static method Max that is defined in System.Math omitting the class name prefix like "(double x, double y) => Max(x, y)". To support this use case, you will need to provide a IMethodResolver that will resolve the Max as System.Math.Max:

// Set the expression to parse
var expToParse = "(double x, double y) => Max(x, y)";

// We need to create the parser with a StaticMethodResolver that will resolve methods with
// the System.Math class.
var expressionParser = new ExpressionParser(
    methodResolver: new StaticMethodResolver(typeof(Math)));

// We can just parse the expression.
var expression = expressionParser.Parse<Func<double, double, double>>(expToParse);

Expression transformation

Inline C# Lambda expression

One parameter expression

In the case where we would like the replace the parameter a from one expression like "a => a * 2" by another expression like "b => b + 1" resulting in "b => (b + 1) * 2", we can use the SingleParameterInliner.

// Setup the expressions to use as input
Expression<Func<int, int>> expressionToAmend = a => a * 2;
Expression<Func<int, int>> expressionToInline = b => b + 1;

// create the expression parameter in-liner.
var inliner = new SingleParameterInliner();

// Amend the given expression replacing parameter 'a' with the expression to in-line resulting in the
// lambda 'b => (b + 1) * 2'.
var inlinedExpression = inliner.Amend<Func<int, int>, Func<int, int>>(expressionToInline, expressionToAmend);
Multi parameter expression

In the case where we would like to amend an expression like "(a, b) => a * b" replacing the two parameter 'a' and 'b' with two another expressions:

  • using "c => c + 1" to replace 'a'
  • and using "d => d - 1" to replace 'b'

all this resulting in "(c, d) => (c + 1) * (d - 1)", we can use the MultiParameterInliner.

All it needs is a ParameterResolver that will be used to get the expression to in-line instead of the parameter itself:

// Setup the expressions to use as input
Expression<Func<int, int>> expressionToAmend = (a, b) => a * b;
Expression<Func<int, int>> expressionToInlineForA = c => c + 1;
Expression<Func<int, int>> expressionToInlineForB = d => d - 1;

// create the expression in-liner.
var inliner = new ExpressionInliner();

// Setup the resolver telling that 'a' must be replaced by in-lined 'c => c + 1' lambda
// and that 'b' must be replaced by in-lined 'd => d - 1' lambda.
var resolver = new ParameterResolver()
    .Register("a", expressionToInlineForA)
    .Register("b", expressionToInlineForB);

// Amend the given expression replacing parameters resulting in the lambda '(c, d) => (c + 1) * (d - 1)'.
var inlinedExpression = inliner.Amend<Func<int, int>, Func<int, int>>(resolver, expressionToAmend);

Inline Constant expression

Let's say that we need to convert a Lambda Expression using an external variable like this:

// Here is a variable we are going to use in a Lambda Expression.
var externalValue = 0.01d;

// The expression is multiplying the input with the variable 'externalValue'.
Expression<Func<double, double>> expToInline = i => i * externalValue;

The use case here is that we want to convert the expression in-lining the actual value of the variable like i => i * 0.01d.

It has never been simpler with the ConstantInliner: all we need is to call its Amend method:

// Create the constant in-liner.
var inliner = new ConstantInliner();

// Amend the expToInline.
var exp = inliner.Amend(expToInline);

// That's yet, 'exp' is equal to 'i => i * 0.01d'

Resolve Property Name

In order to get the name of a property from a lambda like i => i.MyProperty we can use the PropertyNameResolver:

// Create a instance of the resolver.
var resolver = new PropertyNameResolver();

// Resolve the property name of the given lambda.
var name = resolver.GetPropertyName<IMyType, string>(x => x.MyProperty);

// Here name is set to "MyProperty".

Expression serializer

To serialize your expression as a string, you can use the Serialize method like this:

using SoloX.ExpressionTools.Transform;

Expression<Func<double, double, double>> expression = (a, b) => Math.Pow(a, b);

var txt = expression.Serialize();

// will give you the string:
// "(a, b) => Math.Pow(a, b)"