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. Python学习之旅(三十六)

    Python基础知识(35):电子邮件(Ⅱ) 收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上 收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3 Pytho ...

  2. inner_product

    版本1: template < class InputIterator1, class InputIterator2, class T> T inner_product(InputIter ...

  3. myEclipse导入现成项目出现错误 【申明来源于网络】

    myEclipse导入现成项目出现错误 [申明来源于网络] 原地址:http://blog.sina.com.cn/s/blog_6d7703400100znh6.html file–>impo ...

  4. Linux re

    正则表达式并不是一个工具程序,而是一个字符串处理的标准依据,如果想要以正则表达式的方式处理字符串,就得使用支持正则表达式的工具,例如grep.vi.sed.asw等. 注意:ls不支持正则表达式. g ...

  5. iview form 表单的怪异小BUG

    当同一个弹窗中的表单重复利用时: 我原先的代码逻辑是: <Form :label-width="100" class="mt20" ref="c ...

  6. 泡泡一分钟:Semantic Labeling of Indoor Environments from 3D RGB Maps

    张宁 Semantic Labeling of Indoor Environments from 3D RGB Maps Manuel Brucker,  Maximilian Durner,  Ra ...

  7. Redis集合 安装 哨兵集群 配置

    redis相关 redis基础 redis发布订阅 redis持久化RDB与AOF redis不重启,切换RDB备份到AOF备份 redis安全配置 redis主从同步 redis哨兵集群 redis ...

  8. How to enable Linux-PAM on uClinux

    By default the uClinux  uses the tools provided by busybox firstly. So the init login and passwd are ...

  9. hive sqoop,sqoop-hive import data

    https://segmentfault.com/a/1190000002532293 https://www.zybuluo.com/aitanjupt/note/209968 create tab ...

  10. 日期控件——my97

    一.官网 http://www.my97.net/index.asp 下载: //下文已更新与bootstrap样式的结合 二.demo演示 实际使用时请注意文件引入的实际位置: [补充] 数据库字段 ...