〖多次看到有人提起文本表达式的计算问题,就...... 多次看到有人提起文本表达式的计算问题,就动手整理以前的代码并加上注释。 写一个简单的编译器并不是很复杂的,当中要用到些反射的知识。自已觉得,反射的使用在NET中真是无处不在,使用反射没什么效率不效率的问题,毕竟现在的电脑配置并不是很低。适当使用反射,或者通过使用反射本身,会使自己加深对NET的理解。以后会写些运用反射增加代码灵活性的小”文章”供初学者参考。 如果只是计算表达式的值的,当然用不了那么多的代码.这样写法,只是使它通用性强些. 以下的我直接贴代码了,不再说些什么 Imports System.Reflection Imports System.CodeDom Imports System.CodeDom.Compiler Public Class SourceComp '//编译器接口 Private m_Compiler As ICodeCompiler '//编译器参数 Private m_CompilerParameters As CompilerParameters '//引用的程序集 Private m_RefAssemblies As String() = {"System.dll", "System.Data.dll"} '//源代码 Private m_Source As String = "" '//记录是否是默认的源代码 Private m_Is_Default As Boolean = True '//记录编译状态 Private m_Compiled As Boolean = False '//编译生成的程序集 Private m_Assembly As System.Reflection.Assembly '//默认源代码生成的实例 Private m_tmpClass As Object '//默认源代码生成的实例函数 Private m_MethodInfo As System.Reflection.MethodInfo '//默认源代码函数的表达式参数 Private m_Expression As String '//返回程序集 Public ReadOnly Property cpAssembly() As System.Reflection.Assembly Get Return Me.m_Assembly End Get End Property Sub New() '//获取VB编译器实例 Me.m_Compiler = New VBCodeProvider().CreateCompiler '//初始编译器参数 Me.m_CompilerParameters = New CompilerParameters With Me.m_CompilerParameters .GenerateExecutable = False '//False值指定编译为类集,True编译为可执行程序 .GenerateInMemory = False '//只在内存中生成程序集,不输出到磁盘 '//添加默认的程序集 Me.Add_CompilerParameters() End With End Sub '//添加要引用的程序集 Private Sub Add_CompilerParameters() Me.m_CompilerParameters.ReferencedAssemblies.AddRange(Me.m_RefAssemblies) End Sub '//添加指定的引用程序集 Public Sub Add_CompilerParameters(ByVal RefAssemblies As String()) Me.m_RefAssemblies = RefAssemblies Me.m_CompilerParameters.ReferencedAssemblies.Clear() '//清除原有的程序集,重复引用编译会产生异常 Me.Add_CompilerParameters() End Sub '//生成默认的源代码 '//类名:tmpClass '//函数名:GetExpressionValue ,参数:Expression ,参数类型:字符串 '//主要功能:返回表达式Expression的值 ,返回值类型:Object Private Sub BuildDefaultSource() Dim mCodeBuilder As CodeBuilder = New CodeBuilder With mCodeBuilder .AppendCode("Imports System") .AppendCode("Imports System.Data") .AppendCode("Imports System.Math") .AppendCode("Imports Microsoft.VisualBasic") .AppendCode() .AppendCode("Public Class tmpClass") .AppendCode(" Public Function GetExpressionValue() As Object") .AppendCode(" Dim Result As Object") .AppendCode(" Result={0}") '这里传入表达式 .AppendCode(" Return Result") .AppendCode(" End Function") .AppendCode("End Class") End With Me.m_Source = mCodeBuilder.ToString End Sub '//指定源代码 Public Sub SetSource(ByVal Source As String) Me.m_Source = Source Me.m_Compiled = False Me.m_Is_Default = False End Sub '//从指定文件中读取源代码 Public Sub GetSourceFormFile(ByVal SourceFileName As String) Dim mCodeBuilder As CodeBuilder = New CodeBuilder mCodeBuilder.AppendFromFile(SourceFileName) Me.m_Source = mCodeBuilder.ToString Me.m_Compiled = False Me.m_Is_Default = False End Sub '//编译 Public Sub Complile() If Me.m_Source = "" Then Me.BuildDefaultSource() End If If Me.m_Is_Default Then '传入参数 Me.m_Source = String.Format(Me.m_Source, Me.m_Expression) End If Dim mCompResult As CompilerResults = Me.m_Compiler.CompileAssemblyFromSource(Me.m_CompilerParameters, Me.m_Source) '//错误提示 If (mCompResult.Errors.HasErrors) Then Dim ErrorMessage As String ErrorMessage = "编译错误:" & vbCrLf Dim Err As CompilerError For Each Err In mCompResult.Errors ErrorMessage = ErrorMessage & Err.ErrorText & vbCrLf Next Throw New Exception("编译错误: " + ErrorMessage) End If Me.m_Assembly = mCompResult.CompiledAssembly Me.m_Compiled = True End Sub '//如果是默认源代码,此函数取表达式的值; '//如果自定义源代码,请参考本函数视实际自己写 Public Function GetExpressionValue(ByVal Expression As String) As Object If Not Me.m_Is_Default Then MsgBox("所用的代码不是默认代码,此函数无效") Return Nothing End If '如果还没有编译则编译 If Not Me.m_Compiled Then '//传入参数表达式作为代码 Me.m_Expression = Expression Complile() '//生成实例,注意类名区分大小写 Me.m_tmpClass = Me.m_Assembly.CreateInstance("tmpClass") '//取函数,注意大小写 m_MethodInfo = Me.m_tmpClass.GetType().GetMethod("GetExpressionValue") End If '//计算结果 Dim Result As Object = m_MethodInfo.Invoke(m_tmpClass, Nothing) '表达式不同时要重新编译 Me.m_Compiled = False Return Result End Function End Class '//格式生成或读取代码的类 Public Class CodeBuilder Private _StringBuilder As System.Text.StringBuilder '//格式,{0}为空格数,{1}代码字串,最后加回车换行 Private Const CodeFormat As String = "{0}{1}" & ControlChars.CrLf Sub New() _StringBuilder = New System.Text.StringBuilder End Sub Public Overloads Sub AppendCode() _StringBuilder.AppendFormat(CodeFormat, Space(0), Space(0)) End Sub Public Overloads Sub AppendCode(ByVal CodeString As String) _StringBuilder.AppendFormat(CodeFormat, Space(0), CodeString) End Sub Public Overloads Sub AppendCode(ByVal CodeFloor As Integer, ByVal CodeString As String) _StringBuilder.AppendFormat(CodeFormat, Space(CodeFloor * 4), CodeString) End Sub '//直接从已有vb文件中读取代码 Public Sub AppendFromFile(ByVal FileName As String) If Not System.IO.File.Exists(FileName) Then MsgBox(FileName & "不存在.") Exit Sub End If Dim tmpStr As String Dim fs As System.IO.FileStream fs = New System.IO.FileStream(FileName, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read) Dim Reader As New System.IO.StreamReader(fs, System.Text.Encoding.Default) tmpStr = Reader.ReadToEnd Reader.Close() fs.Close() _StringBuilder.Append(tmpStr) End Sub '//返回代码串 Public Overrides Function ToString() As String Return _StringBuilder.ToString End Function '//清除原有代码 Public Sub Clear() If _StringBuilder.Length > 0 Then _StringBuilder.Remove(0, _StringBuilder.Length - 1) End Sub End Class 'CodeBuilder '测试 Dim MyComp As SourceComp Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click '如果不重新生成实例,则因重新编译时输出同名的临时程序集,会出错 MyComp = New SourceComp Console.WriteLine(MyComp.GetExpressionValue("Math.Round(Math.SQRT(123 * 456), 2) ").ToString) '结果236.83 MyComp = New SourceComp Console.WriteLine(MyComp.GetExpressionValue("123 * 456 > 12 * 6987").ToString) '结果False End Sub |
|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )
GMT+8, 2024-9-30 07:32 , Processed in 0.247178 second(s), 12 queries , Gzip On, MemCache On.
Powered by Discuz! X3.5
© 2001-2023 Discuz! Team.