C#中基于.NET6的动态编译技术

news2024/11/24 17:06:30

  前几天要解决动态计算问题,尝试着使用了不同的方法。问题是给定一个包含计算的字符串,在程序运行中得到计算结果,当时考虑了动态编译,在网上查了一些资料完成了这项功能,可是基于不同的.NET平台使用的编程代码相差比较大,觉得麻烦就没有使用,用了常规的三种方法,分别是:使用DataTable、使用JavaScript、使用Excel表单元格的计算。

  了解这项技术还是值得的,因为我的项目基于.NET6,也就使用了基于.NET6的动态编译来完成计算字符串的动态编译和结果输出。

  ⑴解决引用问题

  在关闭项目的情况下修改项目文件。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

<ItemGroup>
   <PackageReference Include="Microsoft.Net.Compilers" Version="3.12.0" PrivateAssets="all" />
   <PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="3.12.0" PrivateAssets="all" />
</ItemGroup>

</Project>

  其中ItemGroup节点及内容是添加的。

  保存后再打开项目进行代码编写。

  ⑵添加引用

using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;

  ⑶代码编写

        private void button1_Click(object sender, EventArgs e)
        {
            string StrInputCode=textBox1.Text.Trim();
            string CompileCode = @"
                    using System;
                    public class Calculator
                    {
                        public static double CalculateResult()
                        {
                            double result = "+StrInputCode+@";
                            return result;
                        }
                    }";

            // 创建表示代码中的结构和语法的语法树
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(CompileCode);

            // 创建了一个C#编译实例,定义编译选项,添加编译引用
            CSharpCompilation compilation = CSharpCompilation.Create("DynamicAssembly")
                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                .AddReferences(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
                .AddReferences(MetadataReference.CreateFromFile(typeof(Action<string>).GetTypeInfo().Assembly.Location)) // 添加对Action的引用
                .AddReferences(MetadataReference.CreateFromFile(typeof(string).GetTypeInfo().Assembly.Location)) // 添加对string的引用
                .AddSyntaxTrees(syntaxTree);

            // 编译代码
            using (MemoryStream ms = new MemoryStream())
            {
                //使用compilation.Emit方法对动态生成的代码进行编译。CompileResult包含编译结果。
                EmitResult CompileResult = compilation.Emit(ms);

                if (CompileResult.Success)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    //使用Assembly.Load方法加载编译后的程序集。
                    Assembly assembly = Assembly.Load(ms.ToArray());
                    //得到类型信息
                    Type type = assembly.GetType("Calculator");
                    //得到方法信息
                    MethodInfo method = type.GetMethod("CalculateResult");
                    //获取计算结果
                    double result1 = (double)method.Invoke(null, null); 
                    // 将计算结果输出到TextBox2中
                    OutputStr(result1.ToString()); 
                }
                else
                {
                    string StrFalse="";
                    foreach (Diagnostic diagnostic in CompileResult.Diagnostics)
                    {
                        StrFalse+= diagnostic.ToString();
                    }
                    //输出编译错误信息
                    textBox2.Text = StrFalse;
                }
            }

        }


        private void OutputStr(string text)
        {
            
            if (textBox2.InvokeRequired)
            {
                textBox2.Invoke((MethodInvoker)delegate { textBox2.Text = text; });
            }
            else
            {
                textBox2.Text = text;
            }
        }

  虽然可以得到正确的结果,但是因为使用的是双精度变量接收结果可能出现结果误差,比如输入1+3-2.2,正确结果应该是1.8,实际输出却是1.7999999999999998;另外,编译的速度也不理想,因为程序中参与运算的量比较大,这一点很成问题了。

  也因为如此,担心计算偏差,在程序中我没有使用这项技术,使用DataTable比较稳妥。

  上面的程序也可以修改,以便完成更多的需求:

  获取计算公式并定义用户方法:

            string StrInputCode =textBox1.Text.Trim();
            string CompileCode = @"
                using System;

                public class UserClass
                {
                    public static void UserMethod(Action<string> OutputStr)
                    {
                        double Result="+ StrInputCode + @";
                        string StrResult=Result.ToString();
                        OutputStr(StrResult);
                    }
                }";

  在编译成功后获取输出:

                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = Assembly.Load(ms.ToArray());
                    Type type = assembly.GetType("UserClass");
                    MethodInfo method = type.GetMethod("UserMethod", new Type[] { typeof(Action<string>) });
                    method.Invoke(null, new object[] { new Action<string>(OutputStr) });

  程序也可以正常运行并获取正确结果。

  本来是想通过这项技术应对一些后面的需求变更,但是实现起来还是不理想,应对需求变更也可以使用其他的方法,比如依赖注入或者使用委托定义好方法和参数并将这些方法编译到一个DLL中,后面只需要修改方法代码再编译就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1186997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sparksql明明插入了但是表里数据是null

现象 将数据插入表的时候&#xff0c;表里的数据是null 代码 原因 建表语句的时候detail字段的类型写成了bigint&#xff0c;而要插入的数据类型是string&#xff0c;所以把建表语句的字段类型改了然后sql文件重跑就解决了

动态轮换住宅代理是什么?为何需要使用它?

随着越来越多的企业完善网络活动&#xff0c;IP代理的重要性变得显而易见。代理可确保顺利、安全且不受限制地访问互联网的大量资源。在不同类型的代理中&#xff0c;轮换代理脱颖而出&#xff0c;那么他哪里有别于其他IP代理呢&#xff1f; 一、什么是动态轮换代理&#xff1f…

Navicat的使用--mysql

表关系 数据库的操作&#xff0c;表字段的设计&#xff0c;一般都由于图形化界面工具Navicat完成。 而表中数据的增删改查&#xff0c;需要熟悉sql语句。 一对一 一对一&#xff1a;一个A对应一个B&#xff0c;一个B对应一个A 将A或B任意一张表的主键设置为外键 一对多 一…

高性能网络编程 - The C10M problem

文章目录 Pre概述回顾C10K实现C10M的挑战思路总结 Pre 高性能网络编程 - The C10K problem 以及 网络编程技术角度的解决思路 概述 在接下来的10年里&#xff0c;因为IPv6协议下每个服务器的潜在连接数都是数以百万级的&#xff0c;单机服务器处理数百万的并发连接&#xff0…

SpringDataJpa(一)

一、JPA概述 1.1 ORM概述 ORM&#xff08;Object-Relational Mapping&#xff09; 表示对象关系映射。在面向对象的软件开发中&#xff0c;通过ORM&#xff0c;就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联&#xff0c;操作对象就可以直…

【自然语言处理】利用python创建简单的聊天系统

一&#xff0c;实现原理 代码设计了一个简单的客户端-服务器聊天应用程序&#xff0c;建立了两个脚本文件&#xff08;.py文件)&#xff0c;其中有一个客户端和一个服务器端。客户端和服务器之间通过网络连接进行通信&#xff0c;客户端发送消息&#xff0c;服务器端接收消息并…

工业相机基本知识理解:帧率、带宽(数据接口)、图像数据格式

1、帧率&#xff1a;Frame Per Second&#xff0c;单位fps&#xff0c;每秒采集的图像数量 2、带宽&#xff1a;一般单位用Gbps&#xff0c;每秒能传输的Gbit数据量 Gige&#xff1a;千兆网&#xff0c;带宽1Gbps USB3.0&#xff1a;带宽5Gbps&#xff0c;一般U3V工业相机用到3…

Redis笔记 Redis主从同步

文章目录 Redis主从搭建主从架构主从数据同步原理全量同步增量同步repl_backlog原理 主从同步优化小结 Redis主从 搭建主从架构 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离。 主从数据…

10 # 手写 every 方法

every 使用 every() 方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。 ele&#xff1a;表示数组中的每一个元素index&#xff1a;表示数据中元素的索引array&#xff1a;表示数组 <script>var arr [1, 3, 5, 7, 8];var result arr.ever…

网络通信——与Socket交换数据(三十一)

1. 与Socket交换数据 1.1 知识点 &#xff08;1&#xff09;通过Android与Socket完成基本的Echo程序实现&#xff1b; &#xff08;2&#xff09;通过对象序列化进行大数据的传输&#xff1b; 1.2 具体内容 对于网络的开发而言&#xff0c;最常使用的交互模式&#xff1a;W…

ZZ308 物联网应用与服务赛题第F套

2023年全国职业院校技能大赛 中职组 物联网应用与服务 任 务 书 &#xff08;F卷&#xff09; 赛位号&#xff1a;______________ 竞赛须知 一、注意事项 1.检查硬件设备、电脑设备是否正常。检查竞赛所需的各项设备、软件和竞赛材料等&#xff1b; 2.竞赛任务中所使用…

【MySQL篇】数据库角色

前言 数据库角色是被命名的一组与数据库操作相关的权限&#xff0c;角色是权限的集合。因此&#xff0c;可以为一组具有相同权限的用户创建一个角色&#xff0c;使用角色来管理数据库权限可以简化授权的过程。 CREATE ROLE&#xff1a;创建一个角色 GRANT&#xff1a;给角色授…

个人前端编程技巧总结

目录 1. 让界面位于当前屏幕的中心&#xff08;屏幕中心&#xff09;css代码示例 2. 界面透明但是内部元素不透明&#xff08;毛玻璃&#xff09;css代码示例 3. 将当前界面的参数传递到跳转的目标页面&#xff08;携参跳转&#xff09;js代码 1. 让界面位于当前屏幕的中心&…

cmake 之add_definitions使用误区

需求 需要实现&#xff0c;在cmake中定义宏定义&#xff0c;可以&#xff1a;1&#xff09; 在code中可以使用&#xff1b;2&#xff09; 在cmake中可以识别是否已定义 问题 宏定义&#xff0c;cmake有add_definitions函数&#xff0c;直观的实现方法如下。 cmake_minimum…

java--实体javaBean

1.什么是实体类 1.就是一种特殊形式的类 2.这个类中的成员变量都要私有&#xff0c;并且要对外提供相应的getXXX&#xff0c;setXXX方法 3.类中必须要有一个公共的无参的构造器 2.实体类有啥应用场景 实体类只负责数据存取&#xff0c;而对数据的处理交给其他类来完成&…

【数据结构】线性表的链式存储结构

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 顺序存储结构的不足的解决办法 从上一节我们对顺序表的讨论中可见,线性表的顺序存储结构的特点是: 逻辑关系上相邻的两个元素在物理位置(内存)上也相邻,因此可以随机存取表中…

妙手ERP功能更新:Shopee认领配置中的主货号支持按后缀自动递增、Ozon采集箱支持批量编辑【颜色样本图】、TikTok Shop......

为了给卖家朋友带来更好的使用体验&#xff0c;更高效地运营跨境店铺&#xff0c;妙手ERP在上周优化了以下多项功能。 01、产品模块优化 全平台 - 系统新增密码错误被锁提示 Shopee - 认领配置中的主货号&#xff08;父SKU&#xff09;支持按后缀自动递增 - 发布配置中的【定…

通过SOLIDWORKS Composer让自定义视图更智能

SOLIDWORKS Composer是一款专业的技术文档创建工具&#xff0c;通过SOLIDWORKS Composer可以快速创建所需的技术文档&#xff0c;无论是用于装配说明&#xff0c;维护手册还是销售展示。 当使用SOLIDWORKS Composer创建交互式内容的时候&#xff0c;自定义视图至关重要。自定义…

DAY47 198.打家劫舍 + 213.打家劫舍II + 337.打家劫舍 III

198.打家劫舍 题目要求&#xff1a;你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警…

前端-选中DOM定位源代码

用到的工具&#xff1a;react-dev-inspector 使用流程 根据react-dev-inspector文档进行配置 安装 yarn add --dev react-dev-inspector配置&#xff1a;在根目录下配置Inspector import { createRoot } from react-dom/client import { Inspector } from react-dev-inspe…