Roslyn 是以 API 为驱动的下一代编译器,集成在最新版的 Visual Studio 上。它开放 C# 和 Visual Basic 编译器的 API,使得开发者可以借助编译器进行解析代码文件、动态为编程语言增加功能、扩展编译器、自定义编译器动作等操作。

将Roslyn编译结果保存在流中,用程序集加载方法将流加载到当前程序集中,就可以在当前的程序集中调用了。

Roslyn支持两种方式的动态编译:

源代码动态编译就是对C#或VB.Net原代码进行解析编译,源代码动态编译实现简单易于上手,但是编译效率较低,适合小量的动态编译工作和初期开发人员。

源代码动态编译示例:

SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
}");
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
}; CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
ms.Seek(, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
}

这样就成功编译了一个动态程序集,这个程序集引用了当前运行时的程序集,在动态程序集中可以引用当前程序集的命名空间,通过下面的反射就可以调用这个动态程序集了;

Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });

Roslyn提供了一系列的API来供开发人员通过调用API的方式来创建一个动态程序集,通过API创建动态程序集的方式开发难度大但是编译效率高,适合需要进行大量动态编译工作的场景,适合高级开发人员,同样以上面实现的动态程序集功能为例,下面是通过API的实现:

SyntaxTree syntaxTree = CompilationUnit()
.WithUsings(
SingletonList<UsingDirectiveSyntax>(
UsingDirective(
IdentifierName("System"))))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
NamespaceDeclaration(
IdentifierName("RoslynCompileSample"))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
ClassDeclaration("Writer")
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
Identifier("Write"))
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)))
.WithParameterList(
ParameterList(
SingletonSeparatedList<ParameterSyntax>(
Parameter(
Identifier("message"))
.WithType(
PredefinedType(
Token(SyntaxKind.StringKeyword))))))
.WithBody(
Block(
SingletonList<StatementSyntax>(
ExpressionStatement(
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("Console"),
IdentifierName("WriteLine")))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList<ArgumentSyntax>(
Argument(
IdentifierName("message")))))))))))))))
.NormalizeWhitespace().SyntaxTree;
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
}; CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
ms.Seek(, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
}

对比两种实现方式的代码可以发现,通过API实现的动态编译就是将原本的所有关键字、标识符、连接符、修饰符、表达式等通过API的方式进行描述。

除了关键字、连接符、修饰符等API外,Roslyn还提供了包括继承、特征、约束等相关API,通过API几乎可以实现任何源码编译能实现的所有功能。

具体列子

生成webapi的接口代理

API

    [Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase, IOrder
{
[HttpGet("{id}")]
public Order Add(int id)
{
return new Order();
} [HttpPut]
public Order Addx(string a)
{
throw new System.NotImplementedException();
} [HttpPut]
public Order Update([FromBody] Order value)
{
return value;
}
}

动态代理

public class ProxyClass
{
static readonly IDictionary<string, Type> services = new ConcurrentDictionary<string, Type>(, ); static ProxyClass()
{
PortsImporter.Ports<IService>();
IEnumerable<Type> typeServices = typeof(IService).Assembly.GetTypes().Where(type =>
{
var typeInfo = type.GetTypeInfo();
return typeInfo.IsInterface && typeInfo.GetCustomAttribute<BundleAttribute>() != null;
}).ToList(); foreach (var typeService in typeServices)
{
string code = GetCode(typeService);
var assembly = GenerateProxyTree(code);
var type = assembly.GetExportedTypes()[];
var fullName = typeService.FullName;
services.Add(fullName, type);
}
} public static T CreateProxy<T>(Type proxyType, object context)
{
return (T)Create(proxyType, context);
} public static object Create(Type proxyType, object context)
{
var instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(null);
return instance;
} public static T Generate<T>()
{
if (services.TryGetValue(typeof(T).FullName, out var type))
{
return CreateProxy<T>(type, null);
}
throw new Exception("未找到实现");
} private static string GetCode(Type typeService)
{
StringBuilder codes = new StringBuilder();
codes.AppendLine("using System;");
codes.AppendLine("using Model;");
codes.AppendLine("using System.Linq;");
codes.AppendFormat("using {0};", typeService.Namespace);
codes.AppendLine();
codes.AppendLine("namespace RoslynCompileSample");
codes.AppendLine("{");
codes.AppendFormat("public class Proxy{0} : {1}", typeService.Name, typeService.Name);
codes.AppendLine();
codes.AppendLine("{");
var methods = typeService.GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (var method in methods)
{
codes.AppendLine();
codes.AppendFormat("public {0} {1} (", method.ReturnType.FullName, method.Name);
List<string> parameterList = new List<string>();
var parameters = method.GetParameters();
foreach (var parameter in parameters)
{
parameterList.Add($"{parameter.ParameterType.FullName} {parameter.Name}");
} codes.Append(string.Join(',', parameterList));
codes.AppendFormat(")");
codes.AppendLine();
codes.AppendLine("{"); #region 需要自己实现的业务代码 /*业务*/
if (method.CustomAttributes.Any(item => item.AttributeType == typeof(HttpGetAttribute)))
{
codes.AppendLine("HttpClientUtility client = new HttpClientUtility(\"http://localhost:57649/api/values\");");
codes.AppendFormat("return client.Get<{0}>(new string[] {{ {1}.ToString() }});", method.ReturnType, parameters.First().Name);
}
else
{
codes.AppendLine("return null;");
} #endregion codes.AppendLine("}");
codes.AppendLine();
} codes.AppendLine("}");
codes.AppendLine("}");
return codes.ToString();
} /// <summary>
/// 万能接口
/// </summary>
/// <param name="code">传入你要实现的代码</param>
/// <returns>动态生成一个程序集</returns>
public static Assembly GenerateProxyTree(string code)
{
Assembly assembly = null;
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyName = Path.GetRandomFileName();
var references = AppDomain.CurrentDomain.GetAssemblies().Select(x => MetadataReference.CreateFromFile(x.Location));
CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (result.Success)
{
ms.Seek(, SeekOrigin.Begin);
assembly = Assembly.Load(ms.ToArray());
}
}
return assembly;
} public static void Tets()
{
//var code = @"using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }";
//var assembly = GenerateProxyTree(code);
//Type type = assembly.GetType("RoslynCompileSample.Writer");
//object obj = Activator.CreateInstance(type);
//type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "打印一句话" });
}
}

测试调用

            /*动态编译*/
var order = ProxyClass.Generate<IOrder>();
var dss = order.Add();

github https://github.com/842549829/Roslyn

Roslyn动态编译的应用场景

用作脚本语言

学习或参与过游戏开发的人基本上都接触过lua语言,Lua是一个非常小巧的脚本语言,而且很容易嵌入其它语言中使用,很多游戏使用lua作为自己的嵌入式脚本语言来实现剧本演进、特效调用、Mod皮肤等功能,脚本语言有着便于免编译局部更新的优点,风靡全球长盛不衰的WOW就大量使用了lua脚本语言来开发前段功能。

.Net有了Roslyn后C#、VB.net也具备了脚本语言的优点,不用预先编译就能够运行,同时又具备了预编译语言的特性,执行效率更高,著名的跨平台游戏开发引擎unity/unity3D就已经提供了C#作为脚本开发语言的支持,一年揽金百亿的王者荣耀就是基于unity3D引擎开发的。

接口的动态实现

随着远程过程调用技术的兴起简化开发过程就成了框架设计开发中需要考虑的重要因素,能像实例化实体类一样的实例化接口并将接口调用映射到远程的真实接口实现是最便捷的调用方式,业务开发人员完全不用考虑网络传输、对象序列化、异步操作等远程

Roslyn动态编译包含了大量的API,限于篇幅关系,Roslyn动态编译的API将在后面的章节进行详细讲解。也可以通过在线Roslyn API生成工具(https://roslynquoter.azurewebsites.net/)进行学习。

Roslyn的更多相关文章

  1. 使用 Roslyn 编译器服务

    .NET Core和 .NET 4.6中 的C# 6/7 中的编译器Roslyn 一个重要的特性就是"Compiler as a Service",简单的讲,就是就是将编译器开放为 ...

  2. 使用roslyn代替MSBuild完成解决方案编译

    原本我是使用批处理调用 MSBuild 完成解决方案编译的,新版的 MSBuild 在 Visual Studio 2015 会自带安装. 当然在Visual Studio 2015 中,MSBuil ...

  3. Solved: “Cannot execute a program. The command being executed was \roslyn\csc.exe”

    When you publish your ASP.NET project to a hosting account such as GoDaddy, you may run into the iss ...

  4. 使用Microsoft Roslyn提取C#和VB.NET源代码中的字符串常量

    Microsoft Roslyn是微软.NET“编译器即服务(Compiler as a Service)”的主要产品,它提供了开放的编译器API,并为源代码产生.分析和重构提供了新一代的语言对象模型 ...

  5. Roslyn 学习笔记(二)

    参考:https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Syntax-Analysis 语法分析过程主要用到以下类或结构: Synt ...

  6. Roslyn 学习笔记(一)

    本文记录了Roslyn开发环境的安装与编译过程,参考了以下Roslyn项目的官方文档 https://github.com/dotnet/roslyn/blob/master/docs/contrib ...

  7. 利用Roslyn构建一个简单的C#交互脚本引擎

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...

  8. 没有技术说明文档的开源都是耍流氓:微软Roslyn编译即服务在CIIP中具体应用(上)

    前段时间我发布了 github开源:企业级应用快速开发框架CIIP WEB+WIN+移动端,很多园友们都表示支持并与我探讨相关技术问题,上篇中我也承诺会写相关的技术文章,本篇就来介绍一下建模模块中使用 ...

  9. c# 编程语言 编译器 Roslyn

    4 月3日,微软向公众发布了Roslyn编译器项目,该项目采用了Apache开源许可协议.C#的创始人 Anders Hejlsberg在Build大会的第二场主题演讲中将这一令人震惊的消息公之于众. ...

  10. VB已死?还是会在Roslyn之下焕发新生?

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 由于最初的ASP.NET 5测试版并未支持VB,导致社区有一种声音:觉得VB将死.今天我们就 ...

随机推荐

  1. css的小知识4

    ---恢复内容开始--- 一.单位 1.px就是一个基本单位     像素 2.em也是一个单位      用父级元素的字体大小乘以em前面的数字.如果父级没有就继承上一个父级直到body,如果bod ...

  2. 解决mapper绑定异常:nested exception is org.apache.ibatis.binding.BindingException:

    原因: 此异常的原因是由于mapper接口编译后在同一个目录下没有找到mapper映射文件而出现的.由于maven工程在默认情况下src/main/java目录下的mapper文件是不发布到targe ...

  3. Javascript htmldecode

    // HtmlDecode http://lab.msdn.microsoft.com/annotations/htmldecode.js // client side version of the ...

  4. Linux命令 printf

    定长: $ printf '%s\t%s\t%s\t%s\t%s\t\n' $(cat printf.txt) # %s 表示一个不固定长度的字符串:printf不是管道命令,因此需要通过类似cat的 ...

  5. [math] sagemath

    官网首页:http://www.sagemath.org 首页里引出的两个教程 http://www.gregorybard.com/Sage.html http://sagebook.gforge. ...

  6. (备忘)打开office2010总是在配置进度

    1.同时按上键盘上面的windows键和R键,出现“运行” 2.输入“regedit”,回车进入注册表 3.点击“HKEY_CURRENT_USER”展开,依次“Software”--“Microso ...

  7. C# Tuple<T1,T2....T>元组的使用

    1) 先说组元:一个数据结构,由通过逗号分割的,用于传递给一个程序或者操作系统的一系列值的组合. NET Framework 直接支持一至七元素的元组 Tuple<T1> Tuple< ...

  8. Python基础(八) yaml在python中的使用

    yaml 通常用来存储数据,类似于json YAML 简介 YAML(Yet Another Markup Language),一种直观的能够被电脑识别的数据序列化格式,是一个可读性高并且容易被人类阅 ...

  9. CentOS 7 之 Systemd 入门教程:实战篇

    开机启动对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件. 如果你想让该软件开机启动,就执行下面的命令(以httpd.ser ...

  10. NOIP2009(codevs1173)最优贸易

    题目大意:给你一张有n个点m条边的有向图,每个点有一个权值,求一条1到n的路径,使得这条路径上存在两个点且他们的权值差最大. 思路:用dis[i]]记录从1到i的路径中所能得到两点间权值差的最大值,然 ...