views:

840

answers:

4

I know there are many algorithms to evaluate expressions, for example

  1. By Recursive Descent
  2. Shunting-yard algorithm
  3. Reverse Polish notation

But is there any way to evaluate any mathematical expression using C# .net reflection? or other modern .net technology?

Thanks a lot

+3  A: 

It's certainly possible. The CodeSnippetCompileUnit class does basically this. I wrote you some example usage code. You'll need to include these namespaces:

  • System.CodeDom.Compiler;
  • System.CodeDom;
  • Microsoft.CSharp;
  • System.Reflection;

Here's the code:

string source = @"
class MyType
{
    public static int Evaluate(<!parameters!>)
    {
        return <!expression!>;
    }
}
";

string parameters = "int a, int b, int c";
string expression = "a + b * c";

string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression);

CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource);
CodeDomProvider provider = new CSharpCodeProvider();

CompilerParameters parameters = new CompilerParameters();

CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);

Type type = results.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");

// The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null.
int result = (int)method.Invoke(null, new object[] { 4, -3, 2 });

Replace 'parameters' and 'expression' by whatever, and you've got yourself a general expression evaluator.

If you get a FileNotFoundException in results.CompiledAssembly, then the snippet failed to compile.

You might also want to take a look at the System.CodeDom.CodeSnippetExpression class. It's used for more specifically reading expressions, but an expression by itself can't be compiled, so you would need to use more CodeDom to build a working class and method around it. This is useful if you want to be able to programmatically manipulate what kind of class you're generating. CodeSnippetCompileUnit is nice to generate an entire working class at once (and simpler for an example) but to manipulate it you would have to do inconvenient string manipulations.

Joren
+2  A: 

Although using compiler services is a simple and efficient solution, it raises serious security issues if the expression is entered by a user, because it could execute virtually anything.

There's another very simple solution that is much more secure : take advantage of the JScript Eval function. You just need to follow these steps :

Create a js file named JsMath.js :

class JsMath
{
    static function Eval(expression : String) : double
    {
        return eval(expression);
    };
}

Compile it into a class library :

jsc /t:library JsMath.js

Reference the JsMath library in your C# project, and use it like that :

double result = JsMath.Eval(expression);
Thomas Levesque
I never even considered security, nor did I know about the JScript eval function. This is also way more concise than my solution. Good answer!
Joren
It's actually possible to access the `eval` function directly from C#, without the intermediate JScript compilation step. See my answer for details.
LukeH
+1  A: 

For me Vici.Parser works extremely well: check it out here , it's the most flexible expression parser I've found so far.

(we've used it to set up 'human-readable' business rules, with data provided by an SQL server database)

Examples are available and there's a very good support by the developer (check the website's forum).

Roel
+3  A: 

Further to Thomas's answer, it's actually possible to access the (deprecated) JScript libraries directly from C#, which means you can use the equivalent of JScript's eval function.

using Microsoft.JScript;        // needs a reference to Microsoft.JScript.dll
using Microsoft.JScript.Vsa;    // needs a reference to Microsoft.Vsa.dll

// ...

string expr = "7 + (5 * 4)";
Console.WriteLine(JScriptEval(expr));    // displays 27

// ...

public static VsaEngine _engine = VsaEngine.CreateEngine();

public static double JScriptEval(string expr)
{
    // error checking etc removed for brevity

    return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
}
LukeH