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成员函数获取): (& ...
随机推荐
- Android组件化开发实践和案例分享
目录介绍 1.为什么要组件化 1.1 为什么要组件化 1.2 现阶段遇到的问题 2.组件化的概念 2.1 什么是组件化 2.2 区分模块化与组件化 2.3 组件化优势好处 2.4 区分组件化和插件化 ...
- 记录--JavaScript 用简约的代码实现一些日常功能
这里给大家分享我在网上总结出来的一些JavaScript 知识,希望对大家有所帮助 一.日期处理 1. 检查日期是否有效 该方法用于检测给出的日期是否有效: const isDateValid = ( ...
- 记录--uniapp 应用APP跳转微信小程序
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近APP项目开发完成,在评审会上老板提了一个需求,想在开发的APP上添加一个链接,可以跳转公司的小程序商城. 原以为会很复杂,结果只有短 ...
- 快速上手系列:CSS
一 选择符 1 通配:*{} 所有元素 类:p{} 如 p 标签等相应元素 ID:#id{} 使用相应 id 属性的元素样式 类:.类名{} 使用相应 class 属性的元素样式 包含:div p{} ...
- C++获取任务管理器信息,封装成DLL,C#调用例子
C++代码 pch.h // pch.h: 这是预编译标头文件. // 下方列出的文件仅编译一次,提高了将来生成的生成性能. // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏 ...
- c# 正则提取内容例子
分类 代码/语法 说明 捕获 (exp) 匹配exp,并捕获文本到自动命名的组里 (?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) ...
- [ROS串口通信]Serial库读入结构体
本文介绍使用c++中Serial库读入结构体: //例如,结构体定义如下: typedef struct __attribute__((packed)) { uint16_t team; /* 本身队 ...
- KingbaseESV8R6表空间与数据库,模式,表的关系
自定义表空间的作用 使用多个表空间可以更灵活地执行数据库操作.当数据库具有多个表空间时,您可以: 1.将用户数据与系统表数据分开存储在不同性能的存储上,以减少I/O争用. 2.将一个应用程序的数据与另 ...
- 双向循环链表(DoubleLoopLinkList)
双向循环链表 关于双向循环链表可以先阅读这篇文章这里就不再赘述:双向链表(DoubleLinkList) Node template<typename T> class Node { pu ...
- 04 jQuery遍历器
04 jQuery遍历器 如果jQuery一次性选择了很多元素节点. 而我们又希望能拿到每一个元素中的相关信息. 此时可以考虑用jQuery的遍历器来完成对元素的循环遍历. 例如: <!DOCT ...