NetCore基于Roslyn的动态编译实现
目录
一. AvalonEdit文本器
1.功能实现
2. 高亮
3. 代码提示
4. 动态编译
1)依赖项初始化
2) 编译函数
二. 运行效果展示
三. 源码链接
四. 参考资料
一. AvalonEdit文本器
1.功能实现
直接用Github上的源码进行实现,icsharpcode/AvalonEdit:The WPF-based text editor component used in SharpDevelop,源码地址见参考资料1,以下是文本器的使用方法
/// <summary>
/// 脚本显示初始化
/// </summary>
private void ScriptInit()
{//文本编辑器定义_editor.FontFamily = new System.Windows.Media.FontFamily("Consolas");_editor.FontSize = 12;_editor.Completion = MyScriptProvider.Instance(new ScriptProvider(), ScriptProvider.GetRelativeAssemblies())._csharpCompletion;_editor.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;_editor.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;if (!string.IsNullOrWhiteSpace(_moduleObj.InCode)){_editor.Text = _moduleObj.InCode;}string fileName = "TEST";_editor.FileName = fileName;_editor.Document.FileName = fileName;elementHost.Child = _editor;elementHost.Dock = DockStyle.Fill;this.splitContainer4.Panel1.Controls.Add(elementHost);//设置高亮_editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
}
注意:
1)文本器CodeTextEditor是基于Control写的,如果是Form窗体需要用ElementHost容器包一下才能使用
2)关闭编译器窗体对象时,需要主动销毁CodeTextEditor和ElementHost,否则即使是托管内存,C#自动销毁特别缓慢,容易造成软件系统因为占用内存较大而卡顿(个人不建议定时器GC回收,原因是对高速项目,GC很容易引起系统短时间卡顿,而带来不可预知的问题,因此对占用较大的托管资源要主动销毁)
3)如果是Wpf的Window调用CodeTextEditor,如果在xaml语言里面写的,就不需要主动释放控件资源
主动释放控件资源代码:
private void button_Cancel_Click(object sender, EventArgs e)
{//主动释放托管内存_editor.Clear();_editor = null;elementHost.Controls.Clear();elementHost.Dispose();elementHost = null;GC.Collect();this.Close();
}
2. 高亮
editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
3. 代码提示
源码中代码提示功能有限,需要结合NRefactory包,实现功能更强大的代码提示,具体参考代码见参考资料2,不过参考资料2中的代码提示,每次输入字符/数字都需要重新查询所有的依赖项内容,造成较大的资源开销,关键代码修改如下(这样,只有输入点.时才会重新查询方法项目)
/// <summary>
/// 文本器提示结果,当result为空时才获取结果
/// </summary>
CodeCompletionResult resultsWhole = null;
private void ShowCompletion(string enteredText, bool controlSpace)
{if (!controlSpace)Debug.WriteLine("Code Completion: TextEntered: " + enteredText);elseDebug.WriteLine("Code Completion: Ctrl+Space");if (Completion == null) {Debug.WriteLine("Code completion is null, cannot run code completion");return;}if (completionWindow == null) {CodeCompletionResult results = null;try {var offset = 0;var doc = GetCompletionDocument(out offset);if (results == null || results.CompletionData.Count == 0 || enteredText == "."){results = Completion.GetCompletions(doc, offset, controlSpace);}else{results = resultsWhole;}} catch (Exception exception) {Debug.WriteLine("Error in getting completion: " + exception);}if (results == null)return;if (completionWindow == null && results != null && results.CompletionData.Any()) {// Open code completion after the user has pressed dot:completionWindow = new CompletionWindow(TextArea);completionWindow.CloseWhenCaretAtBeginning = controlSpace;completionWindow.StartOffset -= results.TriggerWordLength;//completionWindow.EndOffset -= results.TriggerWordLength;IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;foreach (var completion in results.CompletionData.OrderBy(item => item.Text)) {data.Add(completion);}if (results.TriggerWordLength > 0) {//completionWindow.CompletionList.IsFiltering = false;completionWindow.CompletionList.SelectItem(results.TriggerWord);}completionWindow.Show();completionWindow.Closed += (o, args) => completionWindow = null;}}//end if
}//end method
4. 动态编译
CSharpCodeProvider只支持Framework,Roslyn可以同时支持Framwork和netcore,在编译时需要注意内存管理问题(和上述文本器的控件资源主动释放类似),程序集的依赖项List<MetadataReference>特别多时,每次打开脚本对象都会生成这些依赖项,关闭时Net不提供主动释放功能,只能由CLR的自动释放机制管理,而且每次打开脚本对象生成这些依赖项也会占用一定的CPU资源,影响打开速度体验等,所以选择使用静态变量!
private static List<MetadataReference> _references = new List<MetadataReference>();
共用一个依赖项最多的集合,即可,关键代码如下:
1)依赖项初始化
/// <summary>
/// 编译前的初始化,只需要一次
/// </summary>
/// <returns></returns>
public bool RefrenceInit(ref List<MetadataReference> _references)
{
_references = new List<MetadataReference>();
try
{// 元数据引用if (_references == null || _references.Count == 0){_references = new List<MetadataReference>{MetadataReference.CreateFromFile(typeof(System.Uri).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location),MetadataReference.CreateFromFile(typeof(object).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Data.DataTable).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Drawing.Bitmap).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.IO.File).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Windows.Forms.MessageBox).Assembly.Location),MetadataReference.CreateFromFile(typeof(MegaVision.Core.MyMethods).Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Net.HttpWebRequest).Assembly.Location),MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),MetadataReference.CreateFromFile(Assembly.Load("System.Collections").Location),};}Assembly as1 = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "mscorlib.dll");MetadataReference metadataReference = _references.Find((o => o.Display.Contains("mscorlib.dll")));if (_references.Find((o => o.Display.Contains("mscorlib.dll"))) == null){_references.Add(MetadataReference.CreateFromFile(as1.Location));}
}
catch (Exception ex)
{return false;//Log.Error($"C#脚本编译前初始化出错:{ ex.Message}");
}
return true;
}
2) 编译函数
/// <summary>
/// 编译代码
/// </summary>
/// <returns></returns>
public bool Compile()
{if (string.IsNullOrEmpty(Source)){return false;}this._objAssembly = null;this._objectClass = null;// 表达式树SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(Source);// 随机程序集名称string assemblyName = Path.GetRandomFileName();//第三方dll程序集的引用string myDllPath = AppDomain.CurrentDomain.BaseDirectory + "dll\\RunDll\\"; //在这加载自定义的程序集if (Directory.Exists(myDllPath))//判断是否存在{//判断是否是UI.dllforeach (var dllFile in Directory.GetFiles(myDllPath)){try{FileInfo fi = new FileInfo(dllFile);//判断是否是xxxxxxx.dllif (!fi.Name.EndsWith(".dll")) continue;Assembly as2 = Assembly.LoadFrom(fi.FullName);if (_references.Find((o => o.Display == fi.FullName)) == null){_references.Add(MetadataReference.CreateFromFile(as2.Location));}}catch (Exception ex){}}}//optionsCSharpCompilationOptions defaultCompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithOptimizationLevel(OptimizationLevel.Release).WithPlatform(Platform.AnyCpu);if (System.Diagnostics.Debugger.IsAttached){//生成调试信息defaultCompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithOptimizationLevel(OptimizationLevel.Debug).WithPlatform(Platform.AnyCpu);}// 创建编译对象_compilation = CSharpCompilation.Create(assemblyName,syntaxTrees: new[] { syntaxTree },references: _references,options: defaultCompilationOptions);using (var ms = new MemoryStream()){try{// 将编译好的IL代码放入内存流EmitResult result = _compilation.Emit(ms);// 编译失败,提示_compiled = true;int errorNum = 0;StringBuilder sb1 = new StringBuilder();if (!result.Success){IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>diagnostic.IsWarningAsError ||diagnostic.Severity == DiagnosticSeverity.Error);foreach (Diagnostic diagnostic in failures){Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());_compiled = false;errorNum++;if (_isHideInvalidCode){//sb1.Append($"{diagnostic.Id}:【第{diagnostic.Location.SourceSpan.Start - ScriptTemplate.RowCntPreExpression}行:{diagnostic.GetMessage()}】\r\n");sb1.Append($"{diagnostic.Id}:{diagnostic.GetMessage()}】\r\n");}else{//sb1.Append($"{diagnostic.Id}:【第{diagnostic.Location.SourceSpan.Start}行:{diagnostic.GetMessage()}】\r\n");sb1.Append($"{diagnostic.Id}:{diagnostic.GetMessage()}】\r\n");}}}else{// 编译成功,从内存中加载编译好的程序集ms.Seek(0, SeekOrigin.Begin);this._objAssembly = Assembly.Load(ms.ToArray());}ms.Close();ms.Dispose();if (errorNum > 0){_errorText = $"检测出{errorNum}个错误:" + "\r\n" + sb1.ToString();}else{_errorText = "";}if (_compiled){//_objAssembly = assembly;this._objectClass = this._objAssembly.CreateInstance("MyScript");if (this._objectClass != null){this._objectClass.GetType().InvokeMember("ProcessID", BindingFlags.SetProperty, null, this._objectClass, new object[] { ProcessID }); //指定ProcessID属性值this._objectClass.GetType().InvokeMember("ProcessName", BindingFlags.SetProperty, null, this._objectClass, new object[] { ProcessName }); //指定ProcessName属性值this._objectClass.GetType().InvokeMember("ModuleName", BindingFlags.SetProperty, null, this._objectClass, new object[] { ModuleName }); //指定ModuleName属性值}else{_compiled = false;}}return _compiled;}catch (Exception exc){return false;}}
#endif
}
二. 运行效果展示
实现功能:
1. 编辑脚本内容
2. 编译脚本
3.运行脚本
三. 源码链接
CSDN资源
(9条消息) AvalonEdit文本器+NRefactory代码提示+Roslyn动态编译-C#文档类资源-CSDN文库
---------------------------------------------------------------------------------------------------------------------
四. 参考资料
动态编译器的两个github源码,可以学习下:
1. AvalonEdit源码:
GitHub - icsharpcode/AvalonEdit: The WPF-based text editor component used in SharpDevelop
2. AvalonEdit(源码的代码提示部分用NRefactory进行修改)+Roslyn实现动态编译器的demo
GitHub - lukebuehler/NRefactory-Completion-Sample: A small but full featured prototype how to do code completion with NRefactory
NetCore基于Roslyn的动态编译实现相关推荐
- 基于 Roslyn 实现动态编译
基于 Roslyn 实现动态编译 Intro 之前做的一个数据库小工具可以支持根据 Model 代码文件生成创建表的 sql 语句,原来是基于 CodeDom 实现的,最近改成使用基于 Roslyn ...
- 基于roslyn的动态编译库Natasha
人老了,玩不转博客园的编辑器,详细信息转到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg 关键字:Github, NCC, Natasha,Ros ...
- 基于 Roslyn 实现一个简单的条件解析引擎
基于 Roslyn 实现一个简单的条件解析引擎 Intro 最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋 ...
- 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.net standard 的动态编译实现
在前文[基于.net core 微服务的另类实现]结尾处,提到了如何方便自动的生成微服务的客户端代理,使对于调用方透明,同时将枯燥的东西使用框架集成,以提高使用便捷性.在尝试了基于 Emit 中间语言 ...
- netcore 编译 html dll,ASP.NET Core Razor 视图预编译、动态编译
0x01 前言 ASP.NET Core在默认发布情况下,会启动预编译将试图编译成xx.Views.dll,也许在视图中打算修改一处很细小的地方我们需要再重新编译视图进行发布.下面我将从 ASP.NE ...
- 使用Roslyn动态编译和执行
1. 安装nuget package 2.使用Roslyn 动态执行 var engine = new Roslyn.Scripting.CSharp.ScriptEngine();engine.Cr ...
- C#发现之旅第十四讲 基于动态编译的VB.NET脚本引擎
本章说明 在前面章节中,笔者使用了反射和动态编译技术实现了快速ORM框架,在本章中笔者将继续使用这些技术来实现一个VB.NET的脚本引擎,使得人们在开发中能实现类似MS Office那样实现VBA宏的 ...
- 高性能动态编译库Natasha发布1.0版本!
一. 前言 对于开源贡献者,Emit和表达式树不是陌生的字眼,IL的动态特性为封装工作带来了极大的方便,会Emit的开发者可以说驾驭了大部分的高性能.高动态的编程技巧.纵观ef.dapper.json ...
最新文章
- 为AI从业者/研究生/研究员专门定制的全网唯一高端AI训练营
- python语言及其应用下载_Python语言及其应用 中文pdf完整版[13MB]
- 再谈多态——向上映射及VMT/DMT(转)
- c语言程序会使cpu变高,用 C 语言写个程序,运行时,cpu占用率一直保持50%
- 虚拟化记录--No.1
- [vue] 你有使用过vue开发多语言项目吗?说说你的做法?
- python *args和**kwargs以及序列解包
- 屌丝程序员的那些事(一)-毕业那年
- VLfeat win10 vs2015 编译
- kafka跨集群同步方案
- OpenShift 4 - Pod 优先级
- 联想笔记本电脑键盘灯怎么开启_联想笔记本电脑wifi怎么连接 联想笔记本电脑键盘灯开启方法...
- cad计算机绘图实操视频,cad工程制图教程视频
- C语言输出9 * 9口诀。
- 安卓Termux包下载
- 2021年安全员-C证(陕西省)考试资料及安全员-C证(陕西省)免费试题
- Unity UGUI插件,动效UI插件,简约风UI,UI框架,Q弹动画UI,包含Button,Icon,DropDown,InputField,Modal Window单易用有例子。
- 一篇几乎涵盖95%英语语法的文章[Pending]
- python-Django【初级】10天到精通学不会全额退-张子夜-专题视频课程
- VS 调试时,不允许修改代码