65.9K
CodeProject 正在变化。 阅读更多。
Home

VB.NET 中的动态公式计算器/评估器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (9投票s)

2007 年 4 月 20 日

CPOL

1分钟阅读

viewsIcon

76546

这个类可以帮助您根据字符串计算/评估公式,该字符串可以在运行时更改。

引言

这个类可以帮助您根据字符串计算/评估公式,该字符串可以在运行时更改。例如,在“管理信息系统”或“工作流定义规则”中,您可以将决策的公式和相关数据存储在数据存储中,并在您的业务逻辑中使用它。

背景

我正在寻找一种根据来自数据存储的参数计算公式值的方法。例如,我的客户希望编写/更改他的公式并基于此计算值。

Value = Round((P1+P3)/P2)

使用代码

我使用 MSScriptControl.ScriptControlClass 完成这项工作;它帮助我使用 VB 评估 String。这是完整的代码

Public Class Evaluator

    Dim scriptControl As New MSScriptControl.ScriptControlClass
    Private _Formula As String
    Private _VariablesPreffix As String
    Private _Operators As ArrayList
    Private _CorrectedFormula As String
    Private _FormulaVariables As ArrayList
    Private _ParseCondition As ParseCondition = _
             ParseCondition.RaiseErroForUndefinedVariable

    Public ReadOnly Property Formula() As String
    Get
        Return Me._Formula
    End Get
    End Property

    Public ReadOnly Property VariablesPreffix() As String
    Get
        Return Me._VariablesPreffix
    End Get
    End Property

    Private Sub PreEvaluate(ByVal keyCode As String, ByVal keyValue As String)
        Me._Formula = Me._Formula.Replace(keyCode.ToUpper, keyValue)
    End Sub

    Private Sub PreEvaluate(ByVal keyValuesDictionary As Hashtable)
        For Each keyCode As String In keyValuesDictionary.Keys
            Me.PreEvaluate(keyCode, keyValuesDictionary(keyCode))
        Next
    End Sub

    Public Function Evaluate(ByVal values As Hashtable) As Double
        Dim tmpFormula As String = Me._Formula
        For Each var As String In Me._FormulaVariables
            Select Case var.ToLower
            Case "abs", "round", "fix", "sin", "exp", "cos", _
                 "int", "atn","tan","sqr", "sgn", "log"
            Case Else
                Dim index As Integer = CInt(var.Replace(Me._VariablesPreffix, ""))
                If Not values(index) Is Nothing Then
                    tmpFormula = tmpFormula.Replace(var, values(index))
                Else
                    If Me._ParseCondition = ParseCondition.RaiseErroForUndefinedVariable Then
                        Throw New Exception("Undefined variable!" & vbCrLf & _
                              "Base formula: " & Me._Formula & vbCrLf & _
                              "Corrected formula: " & Me._CorrectedFormula & _
                              vbCrLf & "On variable: " & var)
                    Else
                        tmpFormula = tmpFormula.Replace(var, 0)
                    End If
                End If
            End Select
        Next
        Try
            Return CDbl(scriptControl.Eval(tmpFormula))
        Catch ex As Exception
            scriptControl.Error.Clear()
            Return -1
        'Throw New Exception(tmpFormula, ex)
        End Try
    End Function

    Public Sub New(ByVal formula As String, ByVal variablePreffix As String, _
                   ByVal parseCondition As ParseCondition, _
                   Optional ByVal preEvaluateKeyValuesDictionary As Hashtable = Nothing)
        scriptControl.Language = "vbscript"
        Me._Formula = formula
        Me._VariablesPreffix = variablePreffix
        Me._ParseCondition = parseCondition
        InitializeOperators()
        If Not preEvaluateKeyValuesDictionary Is Nothing Then
            Me.PreEvaluate(preEvaluateKeyValuesDictionary)
        End If
        InitializeFormula()
    End Sub

    Private Sub InitializeOperators()
        Me._Operators = New ArrayList
        Me._Operators.Add(CChar("+"))
        Me._Operators.Add(CChar("-"))
        Me._Operators.Add(CChar("/"))
        Me._Operators.Add(CChar("*"))
        Me._Operators.Add(CChar("^"))
        Me._Operators.Add(CChar("("))
        Me._Operators.Add(CChar(")"))
        'Me._Operators.Add(CChar(","))
    End Sub

    Private Sub InitializeFormula()
        Me._CorrectedFormula = Me._Formula.Replace(" ", "")
        Me._FormulaVariables = New ArrayList
        Dim arrOperators() As Char
        Dim variables() As String = _
            Me._CorrectedFormula.Split(CType(Me._Operators.ToArray(GetType(Char)), Char()))
        For Each var As String In variables
            var = var.Trim
            If var.Length > 0 AndAlso Not IsNumeric(var) Then
                Me._FormulaVariables.Add(var)
                If Not IsNumeric(var.Replace(Me._VariablesPreffix, "")) Then
                    Select Case var.ToLower
                     Case "abs", "round", "fix", "sin", "exp", _
                          "cos", "int", "atn","tan", "sqr", "sgn", "log"
                    Case Else
                        Throw New Exception("Wrong formula!" & vbCrLf & _
                                            "Base formula: " & Me._Formula & _
                                            vbCrLf & "Corrected formula: " & _
                                            Me._CorrectedFormula & vbCrLf & _
                                            "On variable: " & var)
                    End Select
                End If
            End If
        Next
    End Sub

    Public ReadOnly Property Operators() As ArrayList
    Get
        Return Me._Operators
    End Get
    End Property

    Public ReadOnly Property ParseCondition() As ParseCondition
    Get
        Return Me._ParseCondition
    End Get
    End Property

End Class

Public Enum ParseCondition
    RaiseErroForUndefinedVariable
    DoNotRaiseErroForUndefinedVariable
End Enum

现在,您可以简单地像这样使用它

Dim calculator As New Evaluator("Round((P1+P3)/P2)", "P", _
               ParseCondition.DoNotRaiseErroForUndefinedVariable)
Dim parameters As New Hashtable
parameters.Add(1, 12)
parameters.Add(2, 24)
parameters.Add(3, 6)
Dim value As Double = calculator.Evaluate(parameters) 

考虑因素

  1. 您应该定义参数前缀以进行识别(例如“P”)。
  2. 不要将预定义函数用作参数前缀。
  3. 如果您没有分配命名参数,它将分配零作为参数的值。
  4. 如果发生错误,它将返回 -1。
  5. 如果您想添加更多函数,您应该在代码的红色行中添加!
  6. 您可以在评估之前添加更多附加参数。
  7. 您还可以在 PreEvaluate 方法中添加您自己的自定义变量。

兴趣

我正在寻找另一种以更好的性能实现此功能的方法…

© . All rights reserved.