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

  前几天要解决动态计算问题,尝试着使用了不同的方法。问题是给定一个包含计算的字符串,在程序运行中得到计算结果,当时考虑了动态编译,在网上查了一些资料完成了这项功能,可是基于不同的.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.hqwc.cn/news/169395.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能基础——图像认知与OpenCV

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

goroutine调度模型 调度策略

文章目录 背景 协程线程与协程的对比线程&#xff08;Thread&#xff09;协程&#xff08;Coroutine&#xff09; 运作线程模型 goroutine调度模型与演进过程G-M模型G-P-M模型抢占式调度器其他优化 调度策略队列轮转系统调用工作量窃取抢占式调度GOMAXPROCS 对性能的影响 Go在语…

MySQL:日志系统

目录 概述错误日志&#xff08;error log&#xff09;慢查询日志&#xff08;slow query log&#xff09;一般查询日志( general log )中继日志&#xff08;relay log&#xff09;Buffer Pool 缓存回滚日志&#xff08;undo log)概述undo log 作用undo log 的存储机制Undo log …

开发者测试2023省赛--Square测试用例

测试结果 官方提交结果 EclEmma PITest 被测文件 [1/7] Square.java /*** This class implements the Square block cipher.** <P>* <b>References</b>** <P>* The Square algorithm was developed by <a href="mailto:Daemen.J@banksys.co…

Linux--vim

一、vim的基础介绍 vim是一个老式的文字处理工具&#xff0c;但是功能很齐全&#xff0c;不仅是文本处理工具&#xff0c;还是一个程序编辑工具&#xff0c;包含了很多额外的功能 为什么Linux使用vim&#xff1f; ①所有类Unix系统都内置vi&#xff0c;而vim相当于是vi的升级版…

MySQL中外键的使用及外键约束策略

一、外键约束的概念 外键约束&#xff08;FOREIGN KEY,缩写FK是数据库设计的一个概念&#xff0c;它确保在两个表之间的关系保持数据的一致性和完整性。 外键是指表中的某个字段的依赖于另一张表中某个字段的值&#xff0c;而被依赖的字段必须具有主键约束或者唯一约束&#…

459. 重复的子字符串

459. 重复的子字符串 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;__459重复的子字符串_枚举__459重复的子字符串_字符串匹配__459重复的子字符串_KMP算法__459重复的子字符串_优化的KMP算法 错误经验吸取 原题链接&#xff1a; 459. …

74hc595模块参考

74hc595模块参考 8位串行并行输出&#xff08;SIPO&#xff09;移位寄存器 使用74HC595移位寄存器扩展微控制器上的输出引脚数量。如果你需要扩充输入引脚的数量那么你需要74HC165移位寄存器。 SER&#xff08;串行输入&#xff09;引脚用于一次一位地将数据发送到移位寄存器…

Flutter的专属Skia引擎解析+用法原理

Skia是一款跨平台的2D图形库&#xff0c;是Google公司开发的&#xff0c;可以用于开发各种应用程序&#xff0c;如浏览器、游戏、移动应用程序等。Skia引擎的主要特点是速度快、可移植性强、占用的内存少、稳定性佳&#xff0c;适用于多种硬件平台。 Skia的目标是提供快速、高…

【Android】画面卡顿优化列表流畅度一

卡顿渲染耗时如图&#xff1a; 卡顿表现有如下几个方面&#xff1a; 网络图片渲染耗时大上下滑动反应慢&#xff0c;甚至画面不动新增一页数据加载渲染时耗时比较大&#xff0c;上下滑动几乎没有反应&#xff0c;画面停止没有交互响应 背景 实际上这套数据加载逻辑已经运行…

统计学_蒙特卡罗方法

1、蒙特卡罗方法的基本思想 蒙特卡罗方法(Monte Carlo method)是由冯诺依曼和乌拉姆等人发明的&#xff0c;“蒙特卡罗”这个名字是出自摩纳哥的蒙特卡罗赌场&#xff0c;这个方法是一类基于概率的方法的统称&#xff0c;不是特指一种方法。 蒙特卡罗方法也成统计模拟方法&am…

计算机msvcp140.dll重新安装的四个解决方法,专门解决dll文件丢失问题的方法

在我多年的电脑使用经历中&#xff0c;曾经遇到过一个非常棘手的问题&#xff0c;那就是电脑提示找不到msvcp140.dll文件。这个问题让我苦恼了很久&#xff0c;但最终还是找到了解决方法。今天&#xff0c;我就来分享一下我解决这个问题的四种方法&#xff0c;希望对大家有所帮…