自动记录数学表达式的最简单方法是什么?

时间:2016-04-05 10:03:17

标签: c# .net

我经常需要在代码中执行一些数学运算。 E.g。

var par1 = 100;
var par2 = 200;
var par3 = 300;

var result = (par1 + par2) / par3;

我还想保存用于记录目的的计算步骤。

    var loggingMessage = string.Format(
      "result = (par1 + par2) / par3; [{0} = ({1} + {2}) / {3}]",
      result,
      par1,
      par2,
      par3
    );

正如我经常这样做,让我疯狂地做这个猴子的工作。这也很难维持。

我知道可以用例如反向抛光记法来完成,但我不想为这些简单的数学动作产生如此大的过度杀伤力。

是否有针对此问题的智能解决方案?

2 个答案:

答案 0 :(得分:4)

嗯,没有太多可以做的来简化这个,但你可以开始使用表达式树:

    var par1 = 100;
    var par2 = 200;
    var par3 = 300;

    Expression<Func<int, int, int, int>> expr = (a, b, c) => (a + b) / c;
    Func<int, int, int, int> func = expr.Compile();

    int result = func(par1, par2, par3);
    string exprAsText = expr.ToString();
    string exprWithValues = exprAsText.Replace("result", result.ToString())
                                      .Replace("a", par1.ToString())
                                      .Replace("b", par2.ToString())
                                      .Replace("c", par3.ToString());

    string logMessage = $"result = {exprAsText} ({result} = {exprWithValues})";
    Console.WriteLine(logMessage);

由于您将ops存储为表达式树,因此您可以轻松地使用某些抽象/泛化来打印这些日志消息。

也许您发现以下代码是一种矫枉过正,但它能够胜任:

// Define your mathematical operations in a static class, and use them
// across your solution
public static class MathOps
{
    public static Expression<Func<int, int, int, int>> SumAndDivide { get; } = (a, b, c) => (a + b) / c;
}

public static class ExpressionExtensions
{
    public static object ExecuteAndLog<TExpr>(this TExpr expr, object args)
        where TExpr : LambdaExpression
    {
        Contract.Requires(expr != null);
        Contract.Requires(args != null);

        IEnumerable<PropertyInfo> argsProperties =
            args.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

        Contract.Assert(expr.Parameters.Count == argsProperties.Count());

        Delegate compiledDelegate = expr.Compile();

        string exprAsText = expr.Body.ToString();
        string replacedExprAsText = (string)exprAsText.Clone();
        List<object> delegateArgs = new List<object>();

        foreach (PropertyInfo property in argsProperties)
        {
            delegateArgs.Add(property.GetValue(args));
            replacedExprAsText = replacedExprAsText.Replace(property.Name, property.GetValue(args)?.ToString());
        }

        object result = compiledDelegate.DynamicInvoke(delegateArgs.ToArray());
        string logMessage = $"result = {exprAsText} ({result} = {replacedExprAsText})";

        // Replace this with your logger
        Trace.WriteLine(logMessage);

        return result;
    }
}

...你可以这样使用它:

class Program
{
    static void Main(string[] args)
    {
        // You give operation arguments as an object. An anonymous
        // object can be enough!
        int result = (int)MathOps.SumAndDivide.ExecuteAndLog
        (
            new
            {
                a = 100,
                b = 200,
                c = 300
            }
        );

        Console.Read();
    }
}

现在轮到你重构了它。也许,在最终解决方案中,我会将数学运算分开并记录到不同的问题,但由于这只是一个答案,我试图尽可能地使用我提出的代码进行压缩;)

工作样本......

...这里:https://dotnetfiddle.net/XloiSC

答案 1 :(得分:0)

我会创建一个包含您需要的操作的类myMaths

public class myMaths
{

    private string _LoggedOperations = "";

    public myMaths()
    {
        _LoggedOperations = "";
    }

    //(A + B) / C
    public double BR_A_plus_B_BR_dev_C(double A, double B, double C)
    {
        double result = (A + B) / C;
        if (_LoggedOperations != "") _LoggedOperations += "\n";
        _LoggedOperations += string.Format("result = (A + B) / C; [{0} = ({1} + {2}) / {3}]", result, A, B, C);
        return result;
    }

    //(A - B) / C
    public double BR_A_minus_B_BR_dev_C(double A, double B, double C)
    {
        double result = (A - B) / C;
        if (_LoggedOperations != "") _LoggedOperations += "\n";
        _LoggedOperations += string.Format("result = (A - B) / C; [{0} = ({1} - {2}) / {3}]", result, A, B, C);
        return result;
    }

    //(A - B) * C
    public double BR_A_minus_B_BR_times_C(double A, double B, double C)
    {
        double result = (A - B) * C;
        if (_LoggedOperations != "") _LoggedOperations += "\n";
        _LoggedOperations += string.Format("result = (A - B) * C; [{0} = ({1} - {2}) * {3}]", result, A, B, C);
        return result;
    }

    //((A + B) * C) / D
    public double BR_BR_A_plus_B_BR_times_C_BR_dev_D(double A, double B, double C, double D)
    {
        double result = ((A + B) * C) / D;
        if (_LoggedOperations != "") _LoggedOperations += "\n";
        _LoggedOperations += string.Format("result = ((A + B) * C) / D; [{0} = (({1} + {2}) * {3}) / {4}]", result, A, B, C, D);
        return result;
    }

    public string LoggedOperations
    {
        get { return _LoggedOperations; }
    }

}

并像这样使用它:

        private void button1_Click(object sender, EventArgs e)
    {
        myMaths m = new myMaths();
        var par1 = 100;
        var par2 = 200;
        var par3 = 300;
        var par4 = 5;
        double result1 = m.BR_A_plus_B_BR_dev_C(par1, par2, par3);
        double result2 = m.BR_A_minus_B_BR_dev_C(par1, par2, par3);
        double result3 = m.BR_A_minus_B_BR_times_C(par1, par2, par3);
        double result4 = m.BR_BR_A_plus_B_BR_times_C_BR_dev_D(par1, par2, par3, par4);

        MessageBox.Show(m.LoggedOperations);
    }