Using Variables

The variables available to an expression can be accessed by the Variables property on the expression context. The VariableCollection object that is returned is basically a dictionary of strings to objects. The first character of a variable name must be a letter or underscore and the following characters can be either letters, underscores, or numbers.

ExpressionContext context = new ExpressionContext();
// Define some variables
context.Variables["a"] = 100;
context.Variables["b"] = 43.2;

// Use the variables in the expression
IDynamicExpression e = context.CompileDynamic("a + b * (a - b)");

Once the expression is compiled and evaluated, the values of the variables can be changed and the expression re-evaluated to get the new result.

Variables act as instances of their type

Because Flee is statically typed, it knows the type of each variable (it infers it by calling GetType() on the value). You can use any public instance methods and properties defined on a variable just as you would in a regular program.

ExpressionContext context = new ExpressionContext();
context.Variables["s"] = "this is a string";
IDynamicExpression e = context.CompileDynamic("s.length + s.Remove(0, 1).length");
int length = (int) e.Evaluate();

Notes
Once a variable is defined, it has an associated type until it is undefined. Creating a variable with an int value and then setting its value to a string will cause a cast exception. Similarly, once an expression is compiled, it assumes that the variable will exist in the collection at evaluation time. Compiling an expression that uses a variable, undefining the variable and then evaluating the expression will cause an error.

Expressions as variables
Expressions can also be used as variables in other expressions. When the top-level expression is evaluated, each sub-expression will be evaluated to provide the variable's value.

ExpressionContext context = new ExpressionContext();
context.Imports.AddType(typeof(Math));
context.Variables.Add("a", 3.14);
IDynamicExpression e1 = context.CompileDynamic("cos(a) ^ 2");
    
context = new ExpressionContext();
context.Imports.AddType(typeof(Math));
context.Variables.Add("a", 3.14);
    
IDynamicExpression e2 = context.CompileDynamic("sin(a) ^ 2");

// Use the two expressions as variables in another expression
context = new ExpressionContext();
context.Variables.Add("a", e1);
context.Variables.Add("b", e2);
IDynamicExpression e = context.CompileDynamic("a + b");
    
object result = e.Evaluate();

On-demand Variables

There are situations where you may not be able to define an expression's variables when it is compiled. In these cases, on-demand variables can be used. If an expression references a variable that doesn't exist in the expression's variables, Flee will raise the ResolveVariableType event. If the event is handled and a type is specified, Flee will then raise the ResolveVariableValue event every time the expression is evaluated. In that event, you have access to the variable's name and type and it is up to you to provide a compatible value.

Here's an example that asks the user to input an expression and then uses on-demand variables to ask the user for the value of each variable in their expression:

static void Main()
{
   ExpressionContext context = new ExpressionContext();
   VariableCollection variables = context.Variables;

   // Hook up the required events
   variables.ResolveVariableType += new EventHandler<ResolveVariableTypeEventArgs>(variables_ResolveVariableType);
   variables.ResolveVariableValue += new EventHandler<ResolveVariableValueEventArgs>(variables_ResolveVariableValue);

   // Get an expression from the user
   Console.Write("Enter an expression: ");
   string s = Console.ReadLine();
   
   // Compile the expression; Flee will query for the types of any variables
   IDynamicExpression e = context.CompileDynamic(s);

   // Evaluate the expression; Flee will query for the values of each variable
   object result = e.Evaluate();
   Console.WriteLine("The result is: {0}", result);
}

// Called when Flee needs to know the type of a variable
static void variables_ResolveVariableType(object sender, ResolveVariableTypeEventArgs e)
{
   // Make all variables Doubles
   e.VariableType = typeof(double);
}

// Called when Flee needs the value of a variable
static void variables_ResolveVariableValue(object sender, ResolveVariableValueEventArgs e)
{
   Console.Write("Enter value for variable '{0}' ({1}): ", e.VariableName, e.VariableType.Name);
   string value = Console.ReadLine();
   e.VariableValue = Convert.ChangeType(value, e.VariableType);
}

Note that Flee raises the resolve events once for every occurrence of a variable in an expression. This means an expression like a + (a * 0.15), will raise both events twice.

Another example
This sample project shows how to use Flee and on-demand variables to compute an expression for each row of a data table where the expression can reference the columns in the current row.

Last edited Dec 13, 2008 at 2:06 AM by ECiloci, version 7

Comments

No comments yet.