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.mfbz.cn/a/126654.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Git】推送Github失败:remote: Permission to xxx/*.git denied to xxx

在github上&#xff0c;创建了token&#xff0c;推送代码报没权限 #设置token git remote set-url origin <your.token>github.com/<your.name>/hello-git.git#推送代码 #git push -u origin main remote: Permission to xxx/hello-git.git denied to xxx. fatal:…

vscode文件跳转(vue项目)

在 .vue 文件中&#xff0c;点击组件名打开 方式1&#xff1a; 在 vue 组件名上&#xff0c;桉住ctrl 鼠标左键 // 重新打开一个tab 方式2&#xff1a; 在 vue 组件名上&#xff0c;桉住ctrl shift 鼠标左键 // 在右侧拆分&#xff0c;并打开一个tab .vue文件的跳转 按住 …

DevChat:VSCode中基于大模型的AI智能编程助手

#AI编程助手哪家好&#xff1f;DevChat“真”好用# 文章目录 1. 前言2. 安装2.1 注册新用户2.2 在VSCode中安装DevChat插件2.3 设置Access Key 3. 实战使用3.1 代码编写3.2 项目创建3.3 代码讲解 4. 总结 1. 前言 DevChat是由Merico公司精心打造的AI智能编程助手。它利用了最先…

Figma切图,轻松上手!

对于UI设计师来说&#xff0c;在设计网页或移动应用界面时&#xff0c;不仅需要考虑视觉效果和用户体验&#xff0c;还需要考虑实际开发过程中的实现。例如&#xff0c;与开发人员合作&#xff0c;将设计草案中的图片、图标、插图等元素转换为网页或移动应用程序的代码&#xf…

PCF8574芯片介绍及驱动方法

文章目录 前言一、PCF8574芯片介绍二、PCF8574读写地址确定三、PCF8574读写模式传输数据四、PCF8574准双向I/O口五、PCF8574驱动程序编写总结 前言 本篇文章带大家学习PCF8574芯片&#xff0c;了解PCF8574芯片有什么作用&#xff0c;以及学习PCF8574的控制方法。 一、PCF8574…

【kafka】Java客户端代码demo:自动异步提交、手动同步提交及提交颗粒度、动态负载均衡

一&#xff0c;代码及配置项介绍 kafka版本为3.6&#xff0c;部署在3台linux上。 maven依赖如下&#xff1a; <!-- kafka --><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka_2.13</artifactId><version>3.6.0…

Dapp开发流程以及应用

随着区块链技术的发展和普及&#xff0c;Dapp&#xff08;去中心化应用&#xff09;逐渐成为了区块链领域中备受关注的话题。Dapp是一种运行在区块链网络上的应用程序&#xff0c;具有去中心化、透明、安全、自治等特点&#xff0c;能够为人们提供更加便捷、高效、安全的应用体…

Stable Diffusion webui 源码调试(一)

Stable Diffusion webui 源码调试&#xff08;一&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ 调试txt2img的参数和工作流 文件 /work/stable-diffusion-webui/modules/txt2img…

Rust和isahc库编写代码示例

Rust和isahc库编写的图像爬虫程序的代码&#xff1a; rust use isahc::{Client, Response}; fn main() { let client Client::new() .with_proxy("") .finish(); let url ""; let response client.get(url) .send() …

rtklib进行PPK解算

使用RTKLIB_bin-rtklib_2.4.3&#xff0c; 打开RTKPOST。 配置相关文件的路径&#xff0c;如果没有广播星历&#xff0c;则到武汉大学IGS数据中心 下载。 打开Options&#xff0c;进行配置。 点击执行 解算中 查看成果

使用OkHttp库爬取百度云视频详细步骤

目录 摘要 一、OkHttp库简介 二、爬虫基本概念 三、使用OkHttp库爬取百度云视频 1、发送HTTP请求 2、处理响应 3、下载文件 四、可能遇到的问题及解决方案 五、注意事项 总结与建议 摘要 本文将详细介绍如何使用OkHttp库爬取百度云视频。文章首先简要介绍OkHttp库和…

stm32f407栈溢出导致跑程序异常

栈溢出&#xff0c;固件下载后&#xff0c;会运行异常。如下代码&#xff1a; 代码运行异常&#xff0c;进入debug&#xff0c;发现有hard fault的错&#xff1a; 因为栈已经溢出&#xff0c;一般MCU的栈地址都是向下增长的&#xff0c;stm32也是一样&#xff0c;stm32在启动文…

《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互

QT版本&#xff1a;5.15.2 VS版本&#xff1a;2019 客户端程序主要包含三块&#xff1a;连接服务器&#xff0c;发送消息&#xff0c;关闭客户端 服务端程序主要包含三块&#xff1a;打开消息监听&#xff0c;接收消息并反馈&#xff0c;关闭服务端 1、先打开服务端监听功能 …

Java 设计模式——访问者模式

目录 1.概述2.结构3.案例实现3.1.抽象访问者类3.2.抽象元素类3.3.具体元素类3.4.具体访问者类3.5.对象结构类3.6.测试 4.优缺点5.使用场景6.扩展6.1.分派6.2.动态分配6.3.静态分配6.4.双分派 1.概述 访问者模式 (Visitor Pattern) 是一种行为型设计模式&#xff0c;它用于将数…

jQuery中显示与隐藏

在我们jQuery当中&#xff0c;有多个显示隐藏的方法&#xff0c;本篇介绍一下hide()、show()、toggle() 在我们JS当中&#xff0c;或是CSS当中&#xff0c;我们常用到display:none或block; 在我们jQuery当中&#xff0c;我们该如何实现显示隐藏 在我们jQuery当中&#xff0c;我…

定义无向加权图,并使用Pytorch_geometric实现图卷积

首先定义无向边并定义边的权重 import torch import torch.nn as nn from torch_geometric.nn import GCNConv import torch.nn.functional as F from torch_geometric.data import Dataa torch.LongTensor([0, 0, 1, 1, 2, 2, 3, 4]) b torch.LongTensor([0, 1, 2, 3, 1, 5,…

amazon产品采集数据

导入需要的库&#xff1a;requests&#xff0c;BeautifulSoup&#xff0c;re&#xff0c;chardet requests用于发送HTTP请求&#xff1b;BeautifulSoup用于解析HTML&#xff1b;re用于正则表达式&#xff1b;chardet用于识别网页编码。 定义函数&#xff0c;接受URL参数&#…

c语言初学者用vs还是vscode?

c语言初学者用vs还是vscode? 看是科班还是自学&#xff0c;一般学校会有要求的编译软件&#xff0c;在这两者之间&#xff0c;用VS的居多&#xff0c;一个可能的原因是VS不用自己装环境。 最近很多小伙伴找我&#xff0c;说想要一些 c语言的资料&#xff0c;然后我根据自己从…

Spring的循环依赖问题

文章目录 1.什么是循环依赖2.代码演示3.分析问题4.问题解决5.Spring循环依赖6. 疑问点6.1 为什么需要三级缓存6.2 没有三级缓存能解决吗&#xff1f;6.3 三级缓存分别什么作用 1.什么是循环依赖 上图是循环依赖的三种情况&#xff0c;虽然方式有点不一样&#xff0c;但是循环依…

一个不用充钱也能让你变强的 VSCode 插件!!!

今天给大家推荐一款不用充钱也能让你变强的 vscode 插件 通义灵码&#xff08;TONGYI Lingma&#xff09;&#xff0c;可以称之为 copilot 的替代甜品 &#x1f4aa; 前言 之前一直使用的 GitHub Copilot&#xff0c;虽然功能强大&#xff0c;但是收费相对来说有点贵&#xf…
最新文章