本文将告诉大家在 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 库导出程序集类型的更多相关文章

  1. dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言

    相信有很多伙伴都很喜欢自己造编程语言,在有现代的很多工具链的帮助下,实现一门编程语言,似乎已不是一件十分困难的事情.我利用 SourceGenerator 源代码生成技术实现了一个简易的中文编程语言, ...

  2. 在Linux上编译dotnet cli的源代码生成.NET Core SDK的安装包

    .NET 的开源,有了更多的DIY乐趣.这篇博文记录一下在新安装的 Linux Ubuntu 14.04 上通过自己动手编译 dotnet cli 的源代码生成 .net core sdk 的 deb ...

  3. CSS border三角、圆角图形生成技术简介

    http://www.zhangxinxu.com/wordpress/?p=794 一.前言 利用CSS的border属性可以生成一些图形,例如三角或是圆角.纯粹的CSS2的内容,没有兼容性的问题, ...

  4. 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. ...

  5. 怎样用Eclipse将Java源代码生成可执行文件[转]

    eclipse将java源代码生成jar可执行文件 用eclipse做了一个web项目的自动化测试,自己用的时候倒是很方便,打开eclipse直接运行即可,但是分享给其他小伙伴用的时候就不太方便,希望 ...

  6. dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标

    大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...

  7. Android NDK生成及连接静态库与动态库

    对于Android应用开发,大部分情况下我们使用Java就能完整地实现一个应用.但是在某些情况下,我们需要借助C/C++来写JNI本地代码.比如,在使用跨平台的第三方库的时候:为了提升密集计算性能的时 ...

  8. 生成lua的静态库.动态库.lua.exe和luac.exe

    前些日子准备学习下关于lua coroutine更为强大的功能,然而发现根据lua 5.1.4版本来运行一段代码的话也会导致 "lua: attempt to yield across me ...

  9. VS2013 生成sqlite3动态连接库及sqlite3.dll的调用

    一,生成sqlite3动态连接库1,去sqlite官网上下载最近的sqlite源码包,解压后得到四个文件:shell.c,sqlite3.c,sqlite3.h,sqlite3ext.h此处还需要sq ...

  10. 用NDK生成cURL和OpenSSL库

    最近在用Qt开发Android应用时需要获取https页面内容,但Qt内置的QNetworkAccessManager类只支持下面这些协议(调用其supportedSchemes成员函数获取): (& ...

随机推荐

  1. 深入分析Java中的PriorityQueue底层实现与源码

    本文分享自华为云社区<滚雪球学Java(70):深入理解Java中的PriorityQueue底层实现与源码分析>,作者: bug菌. 环境说明:Windows 10 + IntelliJ ...

  2. web前端工程化合集

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.Git 1. git 和 svn 的区别 git 和 svn 最大的区别在于 git 是分布式的,而 svn 是集中式的.因此我们不能 ...

  3. 使用小皮面板新建站点配置SSL证书

    1.新建站点 2,点开配置->SSL,将证书内容复制进去,点击保存后会在"配置文件"生成一个serve{}代码块 3,删掉默认的serve{},保留经过SSL生成的serve ...

  4. [ROS串口通信]Serial库读入结构体

    本文介绍使用c++中Serial库读入结构体: //例如,结构体定义如下: typedef struct __attribute__((packed)) { uint16_t team; /* 本身队 ...

  5. WARN o.a.t.util.scan.StandardJarScanner - Failed to scan [file:/D:/Mavencangku/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-api.jar] from classloader hierarchy

    1.SpringBoot项目启动突然报错 2024-03-27 14:57:41 [restartedMain] WARN o.a.t.util.scan.StandardJarScanner - F ...

  6. 不到2000字,轻松带你搞懂STM32中GPIO的8种工作模式

    大家好,我是知微! 学习过单片机的小伙伴对GPIO肯定不陌生,GPIO (general purpose input output)是通用输入输出端口的简称,通俗来讲就是单片机上的引脚. 在STM32 ...

  7. 新前言with留言板

    旧博客也是会用的,但是现在只用博客园写博客,平时csdn的东西也会凑凑热闹 欢迎各位julao来留言板留言 /* ID:lemondi1 LANG:C++ TASK:test */ #include ...

  8. 【直播回顾】OpenHarmony知识赋能第五期第一课——精益开源

    4月26日晚上19点,知识赋能第五期第一节课<精益开源--理解设计思维.精益创业.敏捷开发是如何应用到开源项目中>,在OpenHarmony开发者成长计划社群内成功举行. 本期课程,由开源 ...

  9. 使用Apache POI和Jsoup将Word文档转换为HTML

    简介 在现代办公环境中,Word文档和HTML页面都是常见的信息表达方式.有时,我们需要将Word文档转换为HTML格式,以便在网页上展示或进行进一步的处理.本文将介绍如何使用Apache POI库和 ...

  10. MyBatis-Plus 代码生成(新)

    MyBatis-Plus 的代码生成功能十分人性化,即支持通过简单的配置实现,也可以通过自定义模板实现. 这里列出项目中的常用配置供参考,其他配置可以参考官网:https://baomidou.com ...