前言

从 C# 6.0 开始,C# 编译器就从以前由 C++ 实现的 csc.exe 换成了用 C# 重新实现的开放式 API 式编译服务 Roslyn。这个编译器到现在已经替代了老式编译器,从前 WebForm 的动态编译基础 CSharpCodeProvider 就是对 csc.exe 的调用包装,当然也继承了 csc.exe 的所有毛病。比如这是一个完全的黑盒编译器,把源码传进去返回程序集,中间的符号解析,词法、语法分析,优化和生成这些中间步骤究竟发生了什么我们无从知晓。无法作为脚本引擎把 C# 代码作为脚本执行,曾经有过第三方脚本引擎。当然还有不支持 C# 6.0 或更高版本源代码的编译,巨硬是铁了心要强推 Roslyn 逼死 csc.exe 啊,虽然都 9102 年了,csc.exe 早都透心凉了。

到 VS 2019,C# 又多了一个功能,导航到反编译的源代码,虽然之前已经有各种第三方反编译工具和 VS 集成扩展,但总归没有自带的来的方便。之前大名鼎鼎的 dotPeek 也在使用中发现无法反编译框架为 .NetStandard 2.1 和 .NetCoreApp 3.1 的程序集,不知道现在怎么样了。刚好在使用中发现了 “由 ICSharpCode.Decompiler 提供反编译服务”字样,抱着好玩的心态去 Nuget 搜索了一下,居然真的找到了!既然这样,那岂不是能:把 Roslyn 编译的程序集交给 ICSharpCode.Decompiler 反编译的源代码交给 Roslyn 编译的程序集交给 ICSharpCode.Decompiler 反编译的源代码交给…… (→_→)

正文

这两个包的基本使用其实很简单。在项目的管理 Nuget 程序包中搜索并安装 ICSharpCode.Decompiler 和 Microsoft.CodeAnalysis.CSharp.Scripting。

1、Microsoft.CodeAnalysis.CSharp.Scripting 基本使用:

引用命名空间 using Microsoft.CodeAnalysis.CSharp.Scripting;

开始使用:

 //执行表达式,返回强类型结果
int result = await CSharpScript.EvaluateAsync<int>("1 + 2");

就这么简单,已经能执行一个简单的表达式了,连 C# 语句要分号这种规则都不用 \(@^0^@)/

其他各种花式用法:要引用更多命名空间,包括但不限于

 using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
             //处理编译错误
try
{
Console.WriteLine(await CSharpScript.EvaluateAsync("2+"));
}
catch (CompilationErrorException e)
{
Console.WriteLine("2+ : " + string.Join(Environment.NewLine, e.Diagnostics));
} //添加程序集引用
var result1 = await CSharpScript.EvaluateAsync("System.Net.Dns.GetHostName()",
ScriptOptions.Default.WithReferences(typeof(System.Net.Dns).Assembly));
Console.WriteLine("System.Net.Dns.GetHostName() : " + result1); //导入命名空间 using
var result2 = await CSharpScript.EvaluateAsync("Directory.GetCurrentDirectory()",
ScriptOptions.Default.WithImports("System.IO"));
Console.WriteLine("Directory.GetCurrentDirectory() : " + result2); //导入静态类型 using static
var result3 = await CSharpScript.EvaluateAsync("Sqrt(2)",
ScriptOptions.Default.WithImports("System.Math"));
Console.WriteLine("Sqrt(2) : " + result3); //参数化脚本
var globals = new Globals {X = , Y = };
Console.WriteLine("X + Y : " + await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals)); //编译缓存并多次执行脚本
var script = CSharpScript.Create<int>("X*Y", globalsType: typeof(Globals));
script.Compile();
for (int i = ; i < ; i++)
{
Console.WriteLine("No " + (i + ) + " X*Y : " + (await script.RunAsync(new Globals { X = i, Y = i })).ReturnValue);
} //编译脚本为委托
script = CSharpScript.Create<int>("X/Y", globalsType: typeof(Globals));
ScriptRunner<int> runner = script.CreateDelegate();
for (int i = ; i < ; i++)
{
Console.WriteLine("No " + (i + ) + " X/Y : " + await runner(new Globals { X = new Random().Next(,i), Y = new Random().Next(, i) }));
} //运行脚本片段并检查已定义的变量
var state = await CSharpScript.RunAsync<int>("int answer = 42;");
foreach (var variable in state.Variables)
Console.WriteLine($"{variable.Name} = {variable.Value} of type {variable.Type}"); //连接多个片段为一个脚本
var script1 = CSharpScript.
Create<int>("int x = 1;").
ContinueWith("int y = 2;").
ContinueWith("x + y");
Console.WriteLine("x + y : " + (await script1.RunAsync()).ReturnValue);
//获取编译对象以访问所有Roslyn API
//var compilation = script1.GetCompilation(); //从之前的状态继续执行脚本
var state1 = await CSharpScript.RunAsync("int x = 1;");
state1 = await state1.ContinueWithAsync("int y = 2;");
state1 = await state1.ContinueWithAsync("x+y");
Console.WriteLine("x + y : " + state1.ReturnValue); //读取代码文件并执行编译
var file = @"C:\Users\Administrator\source\repos\ConsoleApp2\ConsoleApp2/GenericGenerator.cs";
var originalText = File.ReadAllText(file);
var syntaxTree = CSharpSyntaxTree.ParseText(originalText);//获取语法树
var type = CompileType("GenericGenerator", syntaxTree);//执行编译并获取类型 var transformer = Activator.CreateInstance(type);
var newContent = (string)type.GetMethod("Transform").Invoke(transformer,
new object[] { "某个泛型类的全文,假装我是泛型类 Walterlv<T> is a sb.", }); var aa = new GenericGenerator();
var str = aa.Transform("某个泛型类的全文,假装我是泛型类 Walterlv<T> is a sb.", );
         private static Type CompileType(string originalClassName, SyntaxTree syntaxTree)
{
// 指定编译选项。
var assemblyName = $"{originalClassName}.g";
var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree },
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
// 这算是偷懒了吗?我把 .NET Core 运行时用到的那些引用都加入到引用了。
// 加入引用是必要的,不然连 object 类型都是没有的,肯定编译不通过。
AppDomain.CurrentDomain.GetAssemblies().Where(x=>!string.IsNullOrEmpty(x.Location)).Select(x => MetadataReference.CreateFromFile(x.Location))); // 编译到内存流中。
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms); if (result.Success)
{
ms.Seek(, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
return assembly.GetTypes().First(x => x.Name == originalClassName);
}
throw new Exception(string.Join(Environment.NewLine, result.Diagnostics));
}
}

当然还有这种四不像代码也是可以滴

 //不仅可以在脚本引擎中引用命名空间,还可以在脚本中引用,当然前提是在脚本引擎中引用了相关程序集
using System; //没有 namespace Xxx { /* code */ } 的定义,直接在外面定义一个类
public class Point
{
public int X {get; set;}
public int Y {get; set;} public Point(int x, int y)
{
X = x;
Y = y;
} public override string ToString()
{
return $"({X}, {Y})";
}
} //变量没有定义在任何类或类的方法中,正常的 C# 代码一定会报错,但骚断腿的 CSharpScript.EvaluateAsync<TResult>(string code) 能正常执行
var random = new Random(); int a = random.Next(, );
int b = random.Next(, );
Func<int, int, int> plus = (l, r) => l + r;
//最后这个表达式没有分号结尾,也没有使用变量接收结果。当然,最后这个带返回值的表达式的结果会在 string result = await CSharpScript.EvaluateAsync<string>(code); 的 result 里收到
$"{a}+{b}={plus(a, b)}\r\nPoint : {new Point(random.Next(0, 1000), random.Next(0, 1000))}"

2、ICSharpCode.Decompiler 的基本使用

 using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem; 5 var decompiler = new CSharpDecompiler(typeof(string).Assembly.Location, new DecompilerSettings());
6 var name = new FullTypeName(typeof(string).FullName);
7 var code = decompiler.DecompileTypeAsString(name);

ICSharpCode.Decompiler 的最新版 6.0.1 预览版1 支持最新框架的程序集反编译,同时支持了大多数 C# 特性,包括部分C# 8.0 刚出的新特性。

现在的 .Net Core 真是可以玩出各种骚操作,太好玩了。当然,在下面的演示代码中有一个集成在网站项目的演示,下载后运行 IdentityServer 项目并访问 /OnlineCode/CSharp 和 /DeCompiler/CSharp 就可以查看效果。C# 脚本大多数演示代码在 CSharpScriptDemo 项目中。

效果预览

脚本运行

反编译代码

转载请完整保留以下内容,未经授权删除以下内容进行转载盗用的,保留追究法律责任的权利!

  本文地址:https://www.cnblogs.com/coredx/p/12045104.html

  完整源代码:Github

  里面有各种小东西,这只是其中之一,不嫌弃的话可以Star一下。

C# 编译器 和 反编译器,你要哪个(歪头)? 我全都要(捏拳)!的更多相关文章

  1. 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)转

    vs2010的mfc项目中编译c语言出现错误: "...预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)" 解决方法: 建工程时 建立空项目  ...

  2. gcc编译器参数

    [gcc编译步骤] 1.预处理,生成.i的文件[预处理器cpp] 2.将预处理后的文件转换成汇编语言,生成文件.s[编译器egcs] 3.由汇编变为目标代码(机器代码)生成.o的文件[汇编器as] 4 ...

  3. Tiny语言编译器简单介绍

    1.简单介绍:编译器是将一种语言翻译成还有一种语言的程序.编译器将源程序的代码作为输出,从而产生用目标语言编写的等价程序.比如源码为C/C++等高级语言,那么目标语言就是目标机器的机器代码.也就是能够 ...

  4. gcc编译器常用选项的含义

    -w: 关闭编译时的警告, 也就是编译后不显示任何warning,因此有时编译中会出现一些诸如数据转换之类的可忽略警告, -Wall: 显示编译后所有警告 -W: 显示警告,但是只是显示编译器认为的会 ...

  5. Asp.Net Core Identity 骚断腿的究极魔改实体类

    前言 默认的 Identity 实体类型在大多数时候已经基本够用,很多时候也只是稍微在 IdentityUser 类中增加一些自定义数据字段,比如头像.这次,我要向园友隆重介绍我魔改之后的 Ident ...

  6. Smali语法编程

    Smali背景: Smali,Baksmali分别是指安卓系统里的Java虚拟机(Dalvik)所使用的一种.dex格式文件的汇编器,反汇编器.其语法是一种宽松式的Jasmin/dedexer语法,而 ...

  7. 一切从IL开始

    IL是什么? IL是Intermediate Language的缩写,是.Net代码转化成机器语言的一个中间语言,因此又把IL语言称之为反汇编语言. IL工具有哪些? 俗话说,工欲善其事必先利其器.了 ...

  8. Android ===smail语法总结

    (转载自 网络)smail 语法总结 http://www.blogjava.net/midea0978/archive/2012/01/04/367847.html Smali背景: Smali,B ...

  9. 20145209刘一阳《JAVA程序设计》第一周课堂测试

    第一周课堂测试 1.下列不属于Java后继技术的是(D) A .Android B .JSP C .XML D .Python 2.下列关于Java语言特点的描述,正确的一组是(C) A .面向过程: ...

随机推荐

  1. Dubbo源码分析:Serialization

    背景 顺序化逻缉处理! 类图 获取Serialization对象时序图 序列化

  2. 四行公式推完神经网络BP

    据说多推推公式可以防止老年痴呆,(●ˇ∀ˇ●) 偶尔翻到我N年前第一次推导神经网络的博客居然四页纸,感慨毅力! http://blog.sina.com.cn/s/blog_1442877660102 ...

  3. ccf算法模板

    bellman ford 算法求最短路径 #include <iostream> using namespace std; ; ; // 边, typedef struct Edge{ i ...

  4. Centos 7 命令行版虚拟机安装

    使用VMware创建虚拟机 点击下一步 点击下一步 下一步 选择你要安装的虚拟机是哪种操作系统 选择虚拟机的安装位置 选择处理器 自定义内存 选择网络 下一步 下一步 下一步就可以 自定义磁盘容量 然 ...

  5. 002_Visual Studio (gnuplot)显示数组波形

    视频教程:https://v.qq.com/x/page/e3039v4j6zs.html 资料下载:https://download.csdn.net/download/xiaoguoge11/12 ...

  6. cube.js 新版本试用preosto

    cube.js 新的版本添加了更多的数据库的支持,但是目前cubejs-cli 以及官方文档问题还挺多,使用不清晰,文档有明显的错误 以下演示presto 数据库的使用 环境准备 安装新版本的cube ...

  7. hasura skor 一个pg 的event trigger 扩展

    hasura skor 是一个hasura 团队早期的event triggerpg 扩展,新的推荐使用graphql engine 参考架构 缺点 只有在skor 运行的时候,数据才可以被捕捉处理 ...

  8. 使用terraform 生成自签名证书

    terraform 是一个很不错的基础设施工具,我们可以用来做关于基础设施部署的事情,可以实现基础设施即代码 以下演示一个简单的自签名证书的生成(使用tls provider) main.tf 文件 ...

  9. Python 02 编写代码

    原文:https://www.cnblogs.com/jimmy-share/p/9784219.html 方式: 交互式编程:打开python.exe文件后,直接输入代码即可.文件的位置(我本地): ...

  10. 手把手教你用Python代码实现微信聊天机器人 -- Python wxpy

    关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己. 本篇文章同步微信公众号 欢迎大家关注我的微信公众号:「醉翁猫咪」 来学习了,微信聊天机器人. 环境要求: Windows / Li ...