dotnet 使用增量源代码生成技术的 Telescope 库导出程序集类型
本文将告诉大家在 dotnet 里面使用免费完全开源的基于增量源代码生成技术的 Telescope 库,进行收集导出项目程序集里面指定类型。可以实现性能极高的指定类型收集,方便多模块对接入自己的业务框架
此 Telescope 库是基于最友好的 MIT 协议开源的,免费开源可商用:https://github.com/dotnet-campus/Telescope
在日常开发过程中,也许会有这样的需求:将项目程序集里面的某种特征的类型们收集起来,用于实现自己的业务需求。比如说自己写了某些工作器,这些工作器类型都是继承 IWorker 接口的,此时业务上期望有某个逻辑可以将其收集导出,方便对接到自己业务上的框架
或者是自己写了某些过程过滤器类型,这些过滤器类型都继承 IFilter 接口,期望能够从项目里面导出收集,方便接入 IoC 容器或者是自动注入到过滤框架里面
此时可选的实现方法是通过反射,找到程序集里面满足条件的类型,对齐进行处理。然而反射的性能是不高的,再加上需要扫描一次程序集,性能就更低了。同时扫描程序集可能导致在启动过程中存在性能问题,比如扫描程序集导致更多依赖程序集被立刻加载,从而降低启动性能
本文将和大家介绍的是我所在的 dotnet 职业技术学苑(dotnet campus)组织开源的 Telescope 库。此 Telescope 库原本就是一个预编译库,在源代码生成技术 SourceGenerator 推出之前早已有此功能。有一个小道消息是 dotnet 的源代码生成技术有部分可能也受到到此库的启发(我脸皮是不是有点厚)哈
在 dotnet 推出了 IIncrementalGenerator 增量 Source Generator 源代码生成技术之后,我也对 Telescope 库进行稍微的更改,推出了基于增量源代码生成技术的版本,下面来看看此库的使用方法和功能
按照 dotnet 惯例,先安装 NuGet 库。可以右击项目管理 NuGet 包安装 dotnetCampus.Telescope.SourceGeneratorAnalyzers 库,也可以编辑 csproj 项目文件添加以下代码安装
<PackageReference Include="dotnetCampus.Telescope.SourceGeneratorAnalyzers" Version="0.10.7-alpha17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
这里需要提到的是 Telescope 的基于增量源代码生成技术的版本是完全的开发者工具侧的库,完全是修改源代码而不需要引入额外的库。换句话说就是使用 Telescope 库可以在发布自己的项目的时候,可以不会有额外的 DLL 引入。这对于许多商用项目都是非常棒的,不会让自己的输出被污染,不会让自己的安装包里面包含了其他组织制作的库
当然了,需要再次提醒一下,这个 Telescope 是基于免费的 MIT 协议的,完全可以商用的,无任何纠纷问题,可以放心使用
完成了基础库的安装之后,接下来就来编写演示的代码了。假定项目程序集里面有如下的一些类型,比如名为 Base 的基础类型,以及名为 FooAttribute 的特性。接下来的任务就是找到程序集里面所有继承 Base 基础类型且标记了 FooAttribute 特性的类型
大家可以假想一下,在自己的项目里面,那些需要反射扫描整个项目程序集才能实现的代码,看看能否套用到这里。如果可以的话,那推荐来试试这个 Telescope 库,看能否给你的项目提升一些性能
class Base
{
}
class FooAttribute : Attribute
{
}
为了方便演示,这里再创建两个类型,用来继承 Base 基础类型且标记了 FooAttribute 特性
[FooAttribute]
class F1 : Base
{
}
[FooAttribute]
class F2 : Base
{
}
现在咱的任务是收集项目程序集定义的继承 Base 基础类型且标记了 FooAttribute 特性的类型,如以上的 F1 和 F2 类型
请看一下使用 Telescope 的收集方式的代码
internal partial class Program
{
static void Main(string[] args)
{
foreach (var (type, attribute, creator) in ExportFooEnumerable())
{
}
}
[dotnetCampus.Telescope.TelescopeExportAttribute()]
private static partial IEnumerable<(Type type, FooAttribute attribute, Func<Base> creator)> ExportFooEnumerable();
}
可以看到用法非常简单,只需要一个分部方法,在方法上标记了 TelescopeExportAttribute 特性即可,没有其他多余的侵入代码
可以看到这里的导出代码是通过 partial 的方式实现源代码生成对接的,只需要编写一个 partial 类型,在这个 partial 类型里面包含一个 partial 的方法,要求这个方法有满足条件的导出返回值,再给方法标记特性,即可自动生成导出类型的代码
如以上的代码即可在 Main 里面的遍历找到了 F1 和 F2 两个类型
更具体的用法要求是标记了 TelescopeExportAttribute 特性的方法的返回值有一定的要求。如要求使用的是 IEnumerable 等类型,且里面使用 ValueTuple 方式。这个 ValueTuple 的形式大概固定,格式如下
(Type type, FooAttribute attribute, Func<Base> creator)
首个参数将会返回收集的类型的 Type 值,比如收集到 F1 那将会是 typeof(F1) 的类型。第二个参数表示要求类型继续标记的特性,如此即可让代码可以有更好的控制。第三个参数是 Func<T> 这里的 T 是表示要求收集的类型必须继承的基类型,可以是类型或接口
导出类型的方法名没有要求,方法的修饰也没有要求,也就是可以是 private 也可以是 public 的等等,可以是静态的也可以是非静态的
通过以上的方式即可在增量源代码生成里面生成出自动收集类型的代码,可以规避使用反射带来的性能损耗,同时也能更好的支持 AOT 打包
所生成的代码大概如下
// 这是开发者写的代码
internal partial class Program
{
static void Main(string[] args)
{
foreach (var (type, attribute, creator) in ExportFooEnumerable())
{
}
}
[dotnetCampus.Telescope.TelescopeExportAttribute()]
private static partial IEnumerable<(Type type, FooAttribute attribute, Func<Base> creator)> ExportFooEnumerable();
}
// 这是生成的代码
internal partial class Program
{
private static partial global::System.Collections.Generic.IEnumerable<(global::System.Type type, global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.FooAttribute attribute, global::System.Func<global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.Base> creator)> ExportFooEnumerable()
{
yield return (typeof(global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.F1), new global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.FooAttribute()
{
}, () => new global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.F1());
yield return (typeof(global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.F2), new global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.FooAttribute()
{
}, () => new global::dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.F2());
}
}
以上代码看起来很复杂,其实只是写全命名空间而已。为了让大家看的更方便,我将其命名空间简写,优化之后的代码如下
internal partial class Program
{
private static partial IEnumerable<(Type type, FooAttribute attribute, Func<Base> creator)> ExportFooEnumerable()
{
yield return (typeof(F1), new FooAttribute()
{
}, () => new F1());
yield return (typeof(F2),
new FooAttribute()
{
}, () => new F2());
}
}
除此之外还有许多高级的功能,比如说收集的类型不限于当前项目程序集,也能收集到当前项目的所有依赖项。如果想要收集到依赖程序集里面的类型,可以在 TelescopeExportAttribute 里面加上对 IncludeReferences 属性的设置即可,如以下代码
internal partial class Program
{
[dotnetCampus.Telescope.TelescopeExportAttribute(IncludeReferences = true)]
private static partial IEnumerable<(Type type, FooAttribute attribute, Func<Base> creator)> ExportFooEnumerable();
}
加上了 IncludeReferences 将会自动收集到满足要求的所有类型,包括当前项目引用的程序集。但必须说明的是加上了 IncludeReferences 属性设置为 true 将会在 Telescope 里收集引用的程序集类型,可能导致开发过程中的卡顿,但也只会影响开发人员的构建速度,不会影响到程序在用户设备上的运行速度
导出的方式除了以上介绍的 IEnumerable 配合带三个参数的 ValueTuple 之外,还可以使用以下的导出方法
如导出时去掉标记的特性,如此即是表示只有满足继承基类就会被导出,不需要在类型上存在特殊标记。代码例子如下,以下代码将导出当前程序集项目里面所有继承 Base 类型的非抽象类型
[dotnetCampus.Telescope.TelescopeExportAttribute()]
private static partial IEnumerable<(Type type, Func<Base> creator)> ExportFooEnumerable();
更多关于我博客请参阅 博客导航
dotnet 使用增量源代码生成技术的 Telescope 库导出程序集类型的更多相关文章
- dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言
相信有很多伙伴都很喜欢自己造编程语言,在有现代的很多工具链的帮助下,实现一门编程语言,似乎已不是一件十分困难的事情.我利用 SourceGenerator 源代码生成技术实现了一个简易的中文编程语言, ...
- 在Linux上编译dotnet cli的源代码生成.NET Core SDK的安装包
.NET 的开源,有了更多的DIY乐趣.这篇博文记录一下在新安装的 Linux Ubuntu 14.04 上通过自己动手编译 dotnet cli 的源代码生成 .net core sdk 的 deb ...
- CSS border三角、圆角图形生成技术简介
http://www.zhangxinxu.com/wordpress/?p=794 一.前言 利用CSS的border属性可以生成一些图形,例如三角或是圆角.纯粹的CSS2的内容,没有兼容性的问题, ...
- PHP源代码生成 main/config.w32.h
PHP源代码生成 main/config.w32.h 1.下载php源代码包php-5.4.0.tar.gz,解压到D:\php-5.4.0 2.下载2个必要的包http://xiazai.jb51. ...
- 怎样用Eclipse将Java源代码生成可执行文件[转]
eclipse将java源代码生成jar可执行文件 用eclipse做了一个web项目的自动化测试,自己用的时候倒是很方便,打开eclipse直接运行即可,但是分享给其他小伙伴用的时候就不太方便,希望 ...
- dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标
大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...
- Android NDK生成及连接静态库与动态库
对于Android应用开发,大部分情况下我们使用Java就能完整地实现一个应用.但是在某些情况下,我们需要借助C/C++来写JNI本地代码.比如,在使用跨平台的第三方库的时候:为了提升密集计算性能的时 ...
- 生成lua的静态库.动态库.lua.exe和luac.exe
前些日子准备学习下关于lua coroutine更为强大的功能,然而发现根据lua 5.1.4版本来运行一段代码的话也会导致 "lua: attempt to yield across me ...
- VS2013 生成sqlite3动态连接库及sqlite3.dll的调用
一,生成sqlite3动态连接库1,去sqlite官网上下载最近的sqlite源码包,解压后得到四个文件:shell.c,sqlite3.c,sqlite3.h,sqlite3ext.h此处还需要sq ...
- 用NDK生成cURL和OpenSSL库
最近在用Qt开发Android应用时需要获取https页面内容,但Qt内置的QNetworkAccessManager类只支持下面这些协议(调用其supportedSchemes成员函数获取): (& ...
随机推荐
- python高级技术(进程一)
一 什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实 ...
- 深入分析Java中的PriorityQueue底层实现与源码
本文分享自华为云社区<滚雪球学Java(70):深入理解Java中的PriorityQueue底层实现与源码分析>,作者: bug菌. 环境说明:Windows 10 + IntelliJ ...
- [HTML、CSS]知识点
[版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/130176402 出自[进步* ...
- 原生js实现table的增加删除
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Python 生成二维码的几种方式、生成条形码
一: # 生成地维码 import qrcode import matplotlib.pyplot as plt from barcode.writer import ImageWriter 创建QR ...
- ARM和x86比较
信不信,随便逮住一个人问他知不知道CPU,我想他的答案一定会是肯定的,但是如果你再问他知道ARM和X86架构么?这两者的区别又是什么?绝大多数的人肯定是一脸懵逼.今天小编就带你深入了解CPU的这两大架 ...
- C# MySQL导出表结构到Excel
软件如图,输入基础信息,点击"测试登录" 连接MySQL需要安装驱动,如下图 连接成功如下图 登录成功后,自动获取所有表信息 双击表名称,右侧查看表结构信息 导出表结构效果如下图 ...
- Spring Cloud服务之Nacos作为注册中心与配置中心
1.创建maven父工程管理jar包版本 创建maven骨架,删除多余部分文件.只留pom文件,添加依赖 <packaging>pom</packaging> <pare ...
- #二分,哈希 or dp#洛谷 4398 [JSOI2008]Blue Mary的战役地图
题目 求两个正方形矩阵的最大公共正方形矩阵边长 分析 第一种就是\(dp\): 设\(dp[x1][y1][x2][y2]\)表示第一个正方形矩阵以\((x1,y1)\)为右下角, 第二个正方形矩阵以 ...
- 文档贡献与写作必读-OpenHarmony开发者文档风格指南
在您使用OpenHarmony文档或参与OpenHarmony文档/生态内容贡献时,是否遇到过如下问题: ● 应该使用第一人称还是第二人称来写作? ● Markdown文件应该如何命名? ● 代码块及 ...