calculate result from a string expression dynamically
Asked Answered
A

6

8

Is there a way to calculate the result of a string expression, for example mystring = "2*a+32-Math.Sin(6)" dynamically knowing that a is a variable that I have, may be there is some dynamic solution or using System.Reflection

string mystring = "2*a+32-Math.Sin(6)"`;
decimal result = SomeMethod(mystring,3); // where a = 3 for example
Adventure answered 14/9, 2012 at 20:7 Comment(0)
G
19

How about making javascript calculate your expression?

Type scriptType = Type.GetTypeFromCLSID(Guid.Parse("0E59F1D5-1FBE-11D0-8FF2-00A0D10038BC"));

dynamic obj = Activator.CreateInstance(scriptType, false);
obj.Language = "javascript";

var res = obj.Eval("a=3; 2*a+32-Math.sin(6)");
Grammarian answered 14/9, 2012 at 20:19 Comment(8)
what's that guid, is there a way to avoid thatAdventure
can we use interop between .net & msscript COM ? without that GUIDAdventure
@SéddikLaraba No I don't think. why don't you want to use that guid? what is wrong with it?Grammarian
@SéddikLaraba You can also add a COM reference in your project to create that javascript object. Isn't that easier?Grammarian
does that works on any system, & doesn't require any thing to be registred, cause i don't want my application to fail if that ClassID doesn't exist @GrammarianAdventure
i'll add it as reference that's betterAdventure
@SéddikLaraba Referencing COM object or creating with CLSID, all are equal. Use whatever you want.Grammarian
i've added msscript.ocx to my references, its working great, thank you !!Adventure
M
6

You can use Compute methood of Datatable in SomeMethod in following way:

       static decimal Somemethod(int val)
        {
            var result = (decimal)new DataTable().Compute(string.Format("2*{0}+32-{1}", val, Math.Sin(6)), "");
            return result;
        }

Simply, you can call like this:

        result = Somemethod(3);
Meyerbeer answered 14/9, 2012 at 20:12 Comment(1)
This is the only solution that worked out of the box for me, without having to do any parsing. Really hidden gem in my opinion.Kiangsi
A
5

if found this its working perfectly, sorry for the bother, all I wanted is on the fly compiler. & this what i get

MessageBox.Show(Eval("5*3-Math.Sin(12) + 25*Math.Pow(3,2)").ToString());

public static object Eval(string sCSCode)
    {

        CodeDomProvider icc = CodeDomProvider.CreateProvider("C#");
        CompilerParameters cp = new CompilerParameters();

        cp.ReferencedAssemblies.Add("system.dll");
        cp.ReferencedAssemblies.Add("system.xml.dll");
        cp.ReferencedAssemblies.Add("system.data.dll");
        cp.ReferencedAssemblies.Add("system.windows.forms.dll");
        cp.ReferencedAssemblies.Add("system.drawing.dll");

        cp.CompilerOptions = "/t:library";
        cp.GenerateInMemory = true;

        StringBuilder sb = new StringBuilder("");
        sb.Append("using System;\n");
        sb.Append("using System.Xml;\n");
        sb.Append("using System.Data;\n");
        sb.Append("using System.Data.SqlClient;\n");
        sb.Append("using System.Windows.Forms;\n");
        sb.Append("using System.Drawing;\n");

        sb.Append("namespace CSCodeEvaler{ \n");
        sb.Append("public class CSCodeEvaler{ \n");
        sb.Append("public object EvalCode(){\n");
        sb.Append("return " + sCSCode + "; \n");
        sb.Append("} \n");
        sb.Append("} \n");
        sb.Append("}\n");

        CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
        if (cr.Errors.Count > 0)
        {
            MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText,
               "Error evaluating cs code", MessageBoxButton.OK,
               MessageBoxImage.Error);
            return null;
        }

        System.Reflection.Assembly a = cr.CompiledAssembly;
        object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

        Type t = o.GetType();
        System.Reflection.MethodInfo mi = t.GetMethod("EvalCode");

        object s = mi.Invoke(o, null);
        return s;

    }
Adventure answered 14/9, 2012 at 20:25 Comment(3)
Do you want to create a new assembly(which will use csc internally) everytime sCSCode changes? Just create a loop and see the performance.Grammarian
is it that bad, because i want to use if possible .Net internal methods, if its too expensive, i'll take the javascript solutionAdventure
object o with an instance can be stored for a better performance and invoked laterRead
V
3

Here are some of the most popular approaches that I am aware of for evaluating string expressions dynamically in C#.

Microsoft Solutions

Non-Microsoft solutions (not that there is anything wrong with that)

Veilleux answered 7/10, 2018 at 5:39 Comment(0)
F
2

I don't think there's any native support for what you are asking, however there are a number of ways to accomplish it, none of which are totally trivial but in the order that I think they are easy:

Foamflower answered 14/9, 2012 at 20:24 Comment(0)
C
1

Make abstract class With the methods you want to use to evaluate the expressions. In run time make a new class that inherits that one. I will share the code soon.

as promised here is the code to do it:

    public class BaseClass
    {
    public BaseClass(){}
    public virtual double Eval(double x,double y){return 0;}
}
public class MathExpressionParser
{
    private BaseClass Evalulator=null;
    public MathExpressionParser(){}
    public bool Intialize(string equation)
    {
        Microsoft.CSharp.CSharpCodeProvider cp=new Microsoft.CSharp.CSharpCodeProvider();
        System.CodeDom.Compiler.ICodeCompiler comp=cp.CreateCompiler();
        System.CodeDom.Compiler.CompilerParameters cpar=new CompilerParameters();

        cpar.GenerateInMemory=true;
        cpar.GenerateExecutable=false;
        cpar.ReferencedAssemblies.Add("system.dll");
        cpar.ReferencedAssemblies.Add("EquationsParser.exe");   //Did you see this before;

        string sourceCode="using System;"+
                          "class DrivedEval:EquationsParser.BaseClass" +
                          "{"+   
                                  "public DrivedEval(){}"+
                                  "public override double Eval(double x,double y)"+
                                  "{"+
                                        "return "+ /*Looook here code insertion*/ equation +";"+
                                  "}"+
                          "}";
        //the previouse source code will be compiled now(run time);
        CompilerResults result=comp.CompileAssemblyFromSource(cpar,sourceCode);

        //If there are error in the code display it for the programmer who enter the equation
        string errors="";
        foreach(CompilerError rrr in result.Errors)
        {
            if(rrr.IsWarning)
                continue;
            errors+="\n"+rrr.ErrorText;
            errors+="\n"+rrr.ToString();
        }
        //You Can show error if there in the sourceCode you just compiled uncomment the following
        //MessageBox.Show(errors);
        if(result.Errors.Count==0&&result.CompiledAssembly!=null)
        {
            Type objtype=result.CompiledAssembly.GetType("DrivedEval");
            try
            {
                if(objtype!=null)
                {
                    Evalulator=(BaseClass)Activator.CreateInstance(objtype);
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message,"Error in Creation the object");
            }
            return true;
        }
        else return false;
    }
    public double evaluate(double x,double y)
    {
        if(Evalulator==null)
            return 0.0;
        return this.Evalulator.Eval(x,y);
    }
}

and you will need to do the following simple test to make sure it works:

private void button1_Click(object sender, System.EventArgs e)
{

    if(parser.Intialize(textBox1.Text)==false)
{
    MessageBox.Show("Check equation");
    return;
}
textBox4.Text=(parser.evaluate(double.Parse(textBox2.Text),double.Parse(textBox3.Text))).ToString();
}
Clack answered 14/9, 2012 at 20:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.