热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于SourceGenerators做个AOP静态编织小实验

0.前言**副标题:**无价值人生记录.0:浪费1000%时间去做一个用来节省1%时间的“轮子玩具”(下:AOP实践2So

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlEE8gQA-1594967517868)(https://upload-images.jianshu.io/upload_images/22570485-98961b5b0cd382bf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

0. 前言

**副标题:**无价值人生记录.0:浪费 1000% 时间去做一个用来节省 1% 时间的“轮子玩具”(下:AOP实践2 Source Generators)

上接:用 Roslyn 做个 JIT 的 AOP

作为第二篇,我们基于Source Generators做个AOP静态编织小实验。

内容安排如下:

  • source generators 是什么?

  • 做个达到上篇Jit 一样的效果的demo

  • source generators还存在什么问题?


1. Source Generators 是什么?

1.1 核心目的

开启dotnet平台的编译时元编程功能,

让我们能在编译时期动态创建代码,

同时考虑IDE的集成,让体验更舒适。

展开我们思想的翅膀

我们能以此做各种事情:

  • 生成实体json 等序列化器代码

  • AOP

  • 接口定义生成httpclient调用代码

  • 等等

如下是官方认为会受益的部分功能列表:

  • ASP.Net: Improve startup time

  • Blazor and Razor: Massively reduce tooling burden

  • Azure Functions: regex compilation during startup

  • Azure SDK

  • gRPC

  • Resx file generation

  • System.CommandLine

  • Serializers

  • SWIG


1.2 目前其设计和使用准则


  • 允许开发者能在编译时动态创建添加新代码到我们程序里面

  • 只能新增代码,不能修改已有代码

  • 当无法生成源时,生成器应当产生诊断信息,通知用户问题所在。

  • 可能访问其他文件非c#源代码文件。

  • 无序运行模式,每个生成器都只能拥有相同的输入编译,即不能用其他生成器的生成结果进行再次生成。

  • 生成器的运行类似于分析器。


2. 实验:代理模式的静态编织

2.1 创建一个Source Generators项目


netstandard2.08.0https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json ;$(RestoreAdditionalProjectSources)

2.2 创建SourceGenerator

需要继承 Microsoft.CodeAnalysis.ISourceGenerator

namespace Microsoft.CodeAnalysis{public interface ISourceGenerator{void Initialize(InitializationContext context);void Execute(SourceGeneratorContext context);}}

并通过[Generator]标识启用

所以我们就可以做一个这样的代理生成器:

using Microsoft.CodeAnalysis;using Microsoft.CodeAnalysis.CSharp.Syntax;using Microsoft.CodeAnalysis.Text;using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace AopAnalyzer{[Generator]public class ProxyGenerator : ISourceGenerator{public void Execute(SourceGeneratorContext context){// retreive the populated receiverif (!(context.SyntaxReceiver is SyntaxReceiver receiver))return;try{// 简单测试aop 生成Action beforeCall = (sb, method) => { };Action afterCall = (sb, method) => { sb.Append("r++;"); };// 获取生成结果var code = receiver.SyntaxNodes.Select(i => context.Compilation.GetSemanticModel(i.SyntaxTree).GetDeclaredSymbol(i) as INamedTypeSymbol).Where(i => i != null && !i.IsStatic).Select(i => ProxyCodeGenerator.GenerateProxyCode(i, beforeCall, afterCall)).First();context.AddSource("code.cs", SourceText.From(code, Encoding.UTF8));}catch (Exception ex){// 失败汇报context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("n001", ex.ToString(), ex.ToString(), "AOP.Generate", DiagnosticSeverity.Warning, true), Location.Create("code.cs", TextSpan.FromBounds(0, 0), new LinePositionSpan())));}}public void Initialize(InitializationContext context){// Register a syntax receiver that will be created for each generation passcontext.RegisterForSyntaxNotifications(() => new SyntaxReceiver());}///

/// 语法树定义收集器,可以在这里过滤生成器所需/// internal class SyntaxReceiver : ISyntaxReceiver{internal List SyntaxNodes { get; } = new List();public void OnVisitSyntaxNode(SyntaxNode syntaxNode){if (syntaxNode is TypeDeclarationSyntax){SyntaxNodes.Add(syntaxNode);}}}}}

具体的代理代码生成逻辑:


using Microsoft.CodeAnalysis;using System;using System.Linq;using System.Text;namespace AopAnalyzer{public static class ProxyCodeGenerator{public static string GenerateProxyCode(INamedTypeSymbol type, Action beforeCall, Action afterCall){var sb = new StringBuilder();sb.Append($"namespace {type.ContainingNamespace.ToDisplayString()} {{");sb.Append($"{type.DeclaredAccessibility.ToString().ToLower()} class {type.Name}Proxy : {type.ToDisplayString()} {{ ");foreach (var method in type.GetMembers().Select(i => i as IMethodSymbol).Where(i => i != null && i.MethodKind != MethodKind.Constructor)){GenerateProxyMethod(beforeCall, afterCall, sb, method);}sb.Append(" } }");return sb.ToString();}private static void GenerateProxyMethod(Action beforeCall, Action afterCall, StringBuilder sb, IMethodSymbol method){var ps = method.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}");sb.Append($"{method.DeclaredAccessibility.ToString().ToLower()} override {method.ReturnType.ToDisplayString()} {method.Name}({string.Join(",", ps)}) {{");sb.Append($"{method.ReturnType.ToDisplayString()} r = default;");beforeCall(sb, method);sb.Append($"r = base.{method.Name}({string.Join(",", method.Parameters.Select(p => p.Name))});");afterCall(sb, method);sb.Append("return r; }");}}}

可以看到和之前jit的代码非常相似

2.3 测试一下


2.3.1 新建测试项目

Exenetcoreapp3.1preview //新版本才有哦,现在还未正式发布 //设置为分析器项目

2.3.2 测试代码

using System;namespace StaticWeaving_SourceGenerators{static class Program{static void Main(string[] args){var proxy = new RealClassProxy(); // 对,生成的新代码可以ide里面直接用,就是这么强大,只要编译一次就看的到了var i = 5;var j = 10;Console.WriteLine($"{i} + {j} = {(i + j)}, but proxy is {proxy.Add(i, j)}");Console.ReadKey();}}public class RealClass{public virtual int Add(int i, int j){return i + j;}}}

输出结果:

5 + 10 = 15, but proxy is 16

cpu 和内存,自然完美:

image

3. Source Generators 还有什么严重的缺陷呢?

3.1 不能引入其他程序集,比如nuget包

比如我们引入Newtonsoft.Json,就会造成如下编译异常:

System.IO.FileNotFoundException: 未能加载文件或程序集“Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed”或它的某一个依赖项。系统找不到指定的文件。

这就造成我们很难利用现有的包做各种事情,以及怎么把我们的代码生成器提供给别人使用了

3.2 不能debug(其实我接受这点)

可以通过UT测试 debug的

3.3 生成结果不能查看

对使用生成器的人会比较麻烦,他不知道具体成什么样子了,特别是生成有错误的时候。

4.后记

毕竟该功能还是实验特性,距离完成还有一定距离,

不过这样可以让语言的发展。


推荐阅读
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 如何在HTML中获取鼠标的当前位置
    本文介绍了在HTML中获取鼠标当前位置的三种方法,分别是相对于屏幕的位置、相对于窗口的位置以及考虑了页面滚动因素的位置。通过这些方法可以准确获取鼠标的坐标信息。 ... [详细]
author-avatar
搬地瓜per
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有