Лучший и кратчайший способ оценить математические выражения - PullRequest
17 голосов
/ 17 сентября 2009

Существует множество алгоритмов для вычисления выражений, например:

  1. рекурсивным спуском
  2. Алгоритм Маневрового двора
  3. Обратная польская запись

Есть ли способ оценить какое-либо математическое выражение, используя отражение C # .net или другую современную технологию .net?

Ответы [ 6 ]

19 голосов
/ 17 сентября 2009

В дополнение к ответу Томаса на самом деле можно получить доступ к (устаревшим) библиотекам JScript непосредственно из C #, что означает, что вы можете использовать эквивалент функции eval JScript.

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 double JScriptEval(string expr)
{
    // error checking etc removed for brevity
    return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
}

private static readonly VsaEngine _engine = VsaEngine.CreateEngine();
13 голосов
/ 17 сентября 2009

Это, конечно, возможно. Класс CodeSnippetCompileUnit делает в основном это. Я написал вам пример кода использования. Вам нужно будет включить эти пространства имен:

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

Вот код:

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 });

Замените "параметры" и "выражение" на что угодно, и вы получите общий оценщик выражений.

Если вы получили FileNotFoundException в results.CompiledAssembly, фрагмент не смог скомпилироваться.

Возможно, вы также захотите взглянуть на класс System.CodeDom.CodeSnippetExpression. Он используется для более точного чтения выражений, но само по себе выражение не может быть скомпилировано, поэтому вам нужно будет использовать больше CodeDom для создания рабочего класса и метода вокруг него. Это полезно, если вы хотите иметь возможность программно управлять типом создаваемого вами класса. CodeSnippetCompileUnit удобен для генерации всего рабочего класса сразу (и проще для примера), но для манипулирования им вам придется выполнять неудобные манипуляции со строками.

3 голосов
/ 31 октября 2012

нкалк - лучший. Вы можете найти его в codeplex и в самородке.
NCalc является оценщиком математических выражений в .NET. NCalc может анализировать любое выражение и оценивать результат, включая статические или динамические параметры и пользовательские функции.

3 голосов
/ 17 сентября 2009

Для меня Vici.Parser работает очень хорошо: посмотрите здесь , это самый гибкий анализатор выражений, который я когда-либо видел

(мы использовали его для настройки «понятных человеку» бизнес-правил с данными, предоставляемыми базой данных SQL-сервера)

Примеры доступны и очень хорошая поддержка со стороны разработчика (проверьте форум на сайте).

3 голосов
/ 17 сентября 2009

Хотя использование сервисов компилятора является простым и эффективным решением, оно вызывает серьезные проблемы с безопасностью, если выражение вводится пользователем, поскольку оно может выполнять практически что угодно .

Есть еще один оченьпростое решение, которое намного более безопасно: используйте функцию JScript Eval.Вам просто нужно выполнить следующие действия:

Создать файл js с именем JsMath.js:

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

Скомпилировать его в библиотеку классов:

jsc /t:library JsMath.js

Ссылка наБиблиотека JsMath в вашем C # проекте, и используйте ее так:

double result = JsMath.Eval(expression);
1 голос
/ 25 сентября 2014

Я думаю, что это лучший способ из всех. Ответ Петара Репака удивителен. Использование аргумента 'expression' объекта DataColumn решает невероятно легко:

static double Evaluate(string expression)
{
    var loDataTable = new DataTable();
    var loDataColumn = new DataColumn("Eval", typeof(double), expression);
    loDataTable.Columns.Add(loDataColumn);
    loDataTable.Rows.Add(0);
    return (double)(loDataTable.Rows[0]["Eval"]);
}
...