Extending the functions available to an expression

Flee allows you to extend the functions available for use in its expressions. Because of Flee's design, you are not required to define adapter classes or use delegates. You simply define your functions in a class or use the functions of an existing class and Flee will do the rest. Note that reflection is only used to lookup the functions when the expression is compiled. When the expression is evaluated, the functions are called using IL instructions just like in a compiled assembly.

The following is a guide to the various ways to add custom functions to an expression:

Importing public, static functions

Define a class with all the public, static functions that you want to use:

public static class CustomFunctions
{
   public static int Product(int a, int b)
   {
      return a * b;
   }

   public static int Sum(int a, int b)
   {
      return a + b;
   }
}

Then use the Imports property of the ExpressionContext class to make them available for use in the expression:

ExpressionContext context = new ExpressionContext();
context.Imports.AddType(typeof(CustomFunctions));
context.Variables.Add("a", 100);
context.Variables.Add("b", 200);
 
IDynamicExpression e = context.CompileDynamic("product(a,b) + sum(a,b)");
int result = (int) e.Evaluate();

You can also import all the functions into a namespace:

context.Imports.AddType(typeof(CustomFunctions), "functions");
IDynamicExpression e = context.CompileDynamic("functions.product(a,b) + a - b");

This also works for the built-in .NET types:

ExpressionContext context = new ExpressionContext();
context.Imports.AddType(typeof(Math));
context.Variables.Add("a", 100);

IDynamicExpression e = context.CompileDynamic("cos(a)");
int result = (int) e.Evaluate();

Importing instance functions using variables

Since variables act as instances of their type, you can call any public, instance functions defined on them. Here's an example that demonstrates this:

ExpressionContext context = new ExpressionContext();
context.Variables.Add("rand", new Random());
 
IDynamicExpression e = context.CompileDynamic("rand.nextDouble() + 100");
double result = (double) e.Evaluate();

Importing function using an expression owner

When you attach an expression to an expression owner, you can use any public/non-public static/instance functions on the owner class. The only limitation is that the functions cannot be put in a separate namespace. Here's the previous example, using the Random class as the expression owner:

Random rand = new Random();
ExpressionContext context = new ExpressionContext(rand);

IDynamicExpression e = context.CompileDynamic("nextDouble() + 100");
double result = (double) e.Evaluate();

Type-safety

Flee performs type-checking on the return type and parameters of all imported functions and will throw an ExpressionCompileException if either is not valid in the context of the expression. Flee will also not allow calls to functions that do not return a value.

Last edited Mar 21, 2009 at 6:24 PM by ECiloci, version 17

Comments

PulsarBlow Feb 7, 2012 at 7:20 PM 
@leithfx
You could encapsulate your double in a custom type and exposed this type on the ExpressionOwner (as a Property or a method returning the instanciated type).
This way you will be able to overload the == operator on your custom type and implement the custom behavior you are looking for.

leithfx Jul 7, 2011 at 8:31 PM 
Is it possible to overload the = operator on doubles so that I can make it compare more loosely (i.e because of floating point precision, 1.1 + 2.2 doesnt equal 3.3). I would like to override the = operator to be more of an almost equals behaviour...