`
RednaxelaFX
  • 浏览: 3049075 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

一个简单的计算器——使用System.CodeDom来生成代码

    博客分类:
  • C#
阅读更多
链接:Evaluating Mathematical Expressions by Compiling C# Code at Runtime, by Marcin Cuprjak (aka Vlad Tepes)

本文只是上面链接里的文章的C# 3.0翻新版。主要是引用过来,为了另一篇文章的需要而将System.CodeDom部分的例子分离到这边来讲。
下面的代码里用到了C# 3.0的类型推导(var关键字),也用到了C#一直都有的verbatim string,不过JavaEye这里的语法高亮程序显然没能处理好这部分的高亮……将就看吧。

我们总是有使用桌面计算器的需求。很多时候,我都会找一个带有交互式环境的解释器的脚本语言来充当桌面计算器。
当然我们总是可以直接写一个表达式解析器来做一个完整的计算器,但那样太麻烦了。本文所演示的,是直接使用C#的表达式,动态生成一个能运算用户指定的表达式的小程序。

假如说我们要计算一个函数的值:
z = f(x, y)

但是我们在编译时还不知道f()函数的定义,而且希望由用户在运行时输入函数定义,则我们可以按顺序来做这么几件事:
1、让用户输入一个表达式作为函数的定义
2、把函数在内存里编译
3、让用户输入x和y的值,并调用函数
4、处理函数的返回结果

在.NET里,让现成的编译器来做这件事非常的简单。首先我们定义一个基类,包含有一个计算表达式的函数的stub:
EvaluatorBase.cs
namespace MathEvaluator
{
    public class EvaluatorBase
    {
        public virtual double Eval( double x, double y ) {
            return 0.0; // return dummy value for this base class
        }
    }
}


然后我们定义一个类来实现对表达式的in-memory编译,如下。这里用到了反射和System.CodeDom来生成代码。在Initialize()方法里,假如编译成功则返回真,失败则返回假。留意到“源代码”是一个字符串,中间留了个空,让用户来填充。
Program.cs, part 1
using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace MathEvaluator
{
    public class MathExpressionEvaluator
    {
        private EvaluatorBase m_evaluator;

        public bool Initialize( string expression ) {
            var compiler = new CSharpCodeProvider( );
            var options = new CompilerParameters( );

            // set compile options
            options.GenerateExecutable = false;
            options.GenerateInMemory = true;
            options.ReferencedAssemblies.Add( "System.dll" );
            options.ReferencedAssemblies.Add( this.GetType( ).Assembly.Location );

            // set the source code to compile
            var source =
                @"using System;
                using MathEvaluator;
                public class UserExpressionEvaluator : MathEvaluator.EvaluatorBase {
                    public override double Eval( double x, double y ) {
                        return " + expression + @";
                    }
                }";

            // compile the code, on-the-fly
            var result = compiler.CompileAssemblyFromSource( options, source );

            // print errors
            foreach ( var error in result.Errors )
                Console.WriteLine( error );

            // if compilation sucessed
            if ( ( !result.Errors.HasErrors )
              && ( result.CompiledAssembly != null ) ) {
                var type = result.CompiledAssembly.GetType(
                    "UserExpressionEvaluator" );
                try {
                    if ( type != null )
                        this.m_evaluator =
                            Activator.CreateInstance( type ) as EvaluatorBase;
                } catch ( Exception ex ) {
                    Console.WriteLine( ex );
                    return false; // something went wrong with the type
                }
                return true;
            }

            // compilation failed
            return false;
        }

        public double Eval( double x, double y ) {
            if ( this.m_evaluator != null )
                return this.m_evaluator.Eval( x, y );
            return 0.0;
        }
    }


接下来只要使用上面的类就行。在下面的驱动类里与用户交互,进行输入输出,并计算结果:
Program.cs, part 2
    class Program
    {
        static void Main( string[ ] args ) {
            var evaluator = new MathExpressionEvaluator( );
            Console.WriteLine( "Enter a math expression,"
                + " with 2 parameters available (x and y): " );
            string expression = Console.ReadLine( );
            if ( evaluator.Initialize( expression ) ) {
                Console.Write( "Enter x: " );
                var x = Convert.ToInt32( Console.ReadLine( ).Trim( ) );
                Console.Write( "Enter y: " );
                var y = Convert.ToInt32( Console.ReadLine( ).Trim( ) );
                Console.WriteLine( "The result is: {0}",
                    evaluator.Eval( x, y ).ToString( ) );
            } else {
                Console.WriteLine( "The expression is either unsupported"
                    + " or contains syntax error(s).");
            }
        }
    }
}


试验一下程序的运行:
引用
Enter a math expression, with 2 parameters available (x and y):
x + Math.Sin(y)
Enter x: 1
Enter y: 2
The result is: 1.90929742682568

也验证一下出错时会怎样:
引用
Enter a math expression, with 2 parameters available (x and y):
(x + 1)) / y
d:\temp\lbfxtper.0.cs(5,39) : error CS1002: 应输入 ;
d:\temp\lbfxtper.0.cs(5,39) : error CS1525: 无效的表达式项“)”
The expression is either unsupported or contains syntax error(s).

嘛,我是在简体中文的Windows XP上运行这程序,所以错误提示里出现了中文……


Anyway,作为桌面计算器上面的代码还是太简陋了。本文只是要做个使用System.CodeDom的例子。
在上面的使用场景中,由于我们无法在运行前得知函数的定义,所以有些错误带到了运行时才能发现也不算是坏事。但假如我们使用System.CodeDom生成代码前就能知道完整的源代码是怎样的,那么让源代码放在字符串里显然不够好——类型安全消失了,IDE等开发工具的支持也没了,因为我们所有的待编译代码都在字符串里。
如果有办法既能在运行时才编译代码,又能在编译时就发现语法错误,那就……请期待下一篇文~
分享到:
评论

相关推荐

    c#图书管理系统 数据库SQL2000

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "2.0.0.0")] [Serializable()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Component...

    CodeDom代码生成工具(源码)

    例如,要使用CodeDom生成一个简单的C#类,你可以首先创建一个`CodeCompileUnit`对象,然后添加一个`CodeNamespace`,接着在命名空间中添加`CodeTypeDeclaration`表示类,最后在类中添加`CodeMemberMethod`表示方法。...

    C#_.NET_动态调用webservice的三种方式

    下面是一个简单的使用样例: ```csharp using System; using System.Web.Services.Description; using System.CodeDom; using Microsoft.CSharp; using System.CodeDom.Compiler; class Program { static void ...

    EWSoftware.CodeDom

    Sandcastle 帮助文档 The topics in this section describe the various changes made to the Web Code Providers library over the life of the project.

    动态代码的使用(反射和动态生成类)

    首先,创建编译器实例,然后将字符串编译成一个类,最后使用反射来调用该类中的方法。 动态生成类可以提高代码的灵活性和可维护性,但需要注意的是,动态生成类可能会对性能产生一些影响。因此,在使用动态生成类时...

    一个c#语言做的计算器

    一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,...

    C#高仿QQ截图

    /// 一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX ...

    C# 使用 CodeDom 动态创建应用程序

    .NET 提供了一个System.CodeDom的命名空间,它允许用户动态编译和创建程序集。本文提供了有关如何动态创建程序集的内容。 用途 1、模板化代码生成:生成适用于 ASP.NET、XML Web 服务客户端代理、代码向导、设计器或...

    c#动态读取代码并动态编译

    例如,我们可以使用CSharpCodeProvider创建一个代码提供者,然后将字符串形式的C#代码传入,编译成一个Assembly。 三、Roslyn编译API 微软的Roslyn项目为C#和VB.NET提供了开源的编译器API,这使得动态编译变得更加...

    netframework类库

    每个代码生成器可根据“代码文档对象模型”(CodeDOM) 源代码模型的结构(由System.CodeDom命名空间所提供的元素组成),用特定的某种编程语言生成源代码。 System.Collections:包含定义各种对象集合(如列表、...

    codeDom

    总的来说,这个压缩包可能包含了一个使用CodeDom进行动态代码生成和编译的.NET项目,项目可能涉及Windows Forms应用、VSA脚本支持以及动态生成的源代码。CodeDom的强大之处在于其灵活性和跨语言性,使得开发更加便捷...

    CodeDom摘要介绍

    下面是一个简单的 CodeDom 示例代码片段,用于演示如何使用 CodeDom 创建一个包含命名空间、类和导入语句的代码单元。 ```csharp // 创建 CodeCompileUnit 对象 CodeCompileUnit classCompileUnit = new ...

    动态生成与编译

    为了更好地理解和使用CodeDOM,我们可以通过一个简单的示例来展示其基本用法: ```csharp using System; using System.CodeDom; using System.CodeDom.Compiler; using Microsoft.CSharp; using System.IO; using ...

    .net 生成DLL

    以下是使用System.CodeDom.Compiler命名空间进行动态编译的一个示例: ```csharp using System; using System.CodeDom.Compiler; using System.Data; using System.Xml; using System.Diagnostics; // 创建代码...

    动态调用webservice的模块(c#)

    - 然后,使用`System.CodeDom.Compiler.CodeDomProvider`和`System.CodeDom.Compiler.CompilerResults`来编译这个元数据为实际的代理类。 2. **创建通道工厂:** - 使用`System.ServiceModel.ChannelFactory...

    net Framework下的命名空间.docx

    .NET Framework 是一个全面的开发平台,它包含了丰富的类库,为开发者提供了许多命名空间,以支持不同领域的编程需求。命名空间是一种组织代码的方式,它帮助开发者更好地管理和使用类与方法,避免命名冲突。 1. ...

    CodeDom示例(c#)

    在本示例中,`ClassGenerator`可能是实现CodeDom功能的一个类,用于生成C#类的源代码。通常,`ClassGenerator`会包含一系列方法,用于创建CodeDom对象,如创建`CodeTypeDeclaration`表示类,`CodeMemberMethod`表示...

    经典的.net动态编译、执行、调试 源码

    .NET框架提供了一个强大的工具——`System.CodeDom.Compiler`命名空间,它包含了用于编译源代码的能力。主要使用`CodeDomProvider`类来生成IL(中间语言)代码,然后通过`CompileAssemblyFromSource`或`...

    .net命名空间介绍

    它提供了一系列接口和类来生成和编译源代码。这对于那些需要动态生成和编译代码的应用场景非常有用。 #### System.Collections System.Collections 命名空间提供了各种集合类,包括 List、Dictionary、Queue 等,...

Global site tag (gtag.js) - Google Analytics