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. C语言编程(多线程)

    C语言中多线程编程包括的文件:#include<pthread.h>(linux环境下) pthread_t //线程函数返回类型 pthread_mutrex_t //互斥锁类型 int ...

  2. lucene学习教程

    1Lucene的介绍 ①Lucene是什么: 是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎 ②Lu ...

  3. linux--- python3环境部署篇

    环境部署 我们在pycharm上都是自己设置的python3解释器的环境变量,使得代码能够正常执行!可是怎么能让我们的python代码在LINUX上跑起来呢? linux是内置python,可是内置的 ...

  4. Codeforces 1072 - A/B/C/D - (Done)

    链接:http://codeforces.com/contest/1072/ A - Golden Plate - [计算题] #include<bits/stdc++.h> using ...

  5. openERP笔记 自定义模块开发

    ##需求描述 输入和查询课程,把信息储存到课程对象里 课程包含以下信息:名称,价格,天数,开始日期,教师,学员 每个课程可以有多个学员,要记录学员的姓名.电话.电子邮件 课程可以添加教材和作业等文档附 ...

  6. MAC OSX 开启/禁用SafeSleep功能

    如果想要禁用,我们需要在终端中输入下面的命令: 然后定位到/private/var/vm/删除已经存在的sleepimage文件 cd /private/var/vm/ 使用下面的命令删除该文件 su ...

  7. 43-2-CAN协议

    1.帧的种类 通信是通过以下 5 种类型的帧进行的. • 数据帧 • 遥控帧 • 错误帧 • 过载帧 • 帧间隔 另外, 数据帧和遥控帧有标准格式和扩展格式两种格式.标准格式有 11 个位的标识符(I ...

  8. SpringBoot-将servlet容器变成undertow测试tomcat吞吐量

    将Servlet容器变成Undertow 默认情况下,Spring Boot 使用 Tomcat 来作为内嵌的 Servlet 容器 可以将 Web 服务器切换到 Undertow 来提高应用性能.U ...

  9. Django基本配置与URLconf

    what's the Django python的框架主要有:Django.Flask.Tornado Django是一个开放源代码的Web应用框架,由Python写成.它的主要特点是大而全,我们需要 ...

  10. 唯美MACD-完全版

    前言: 自己很喜欢MACD这个指标,因为很欠缺所以就搜集的多一点,有人问,学习缠为什么还这么搜集些Macd的资料呢?因为在分析走势(或盘整背驰.或趋势背驰)的时候我的习惯使用Macd做辅助判断,所以M ...