使用 Roslyn 获取引用程序集中的接口实现

2024-01-09

我想在我正在开发的框架中绕过一些经典的程序集扫描技术。

因此,假设我定义了以下合同:

public interface IModule
{

}

这存在于说Contracts.dll.

现在,如果我想发现该接口的所有实现,我们可能会执行类似于以下的操作:

public IEnumerable<IModule> DiscoverModules()
{
    var contractType = typeof(IModule);
    var assemblies = AppDomain.Current.GetAssemblies() // Bad but will do
    var types = assemblies
        .SelectMany(a => a.GetExportedTypes)
        .Where(t => contractType.IsAssignableFrom(t))
        .ToList();

    return types.Select(t => Activator.CreateInstance(t));
}

这不是一个很好的例子,但它可以。

现在,这些类型的程序集扫描技术的性能可能非常低下,而且都是在运行时完成的,通常会影响启动性能。

在新的DNX环境中,我们可以使用ICompileModule实例作为元编程工具,因此您可以捆绑一个实现ICompileModule进入你的Compiler\Preprocess文件夹并让它做一些时髦的事情。

我的目标是使用ICompileModule实现,以完成我们在运行时执行的工作,而不是在编译时执行。

  • 在我的参考文献(编译和程序集)和我当前的编译中,发现所有可实例化的实例IModule
  • 创建一个类,我们调用它ModuleList具有生成每个模块的实例的实现。
public static class ModuleList
{
    public static IEnumerable<IModule>() GetModules()
    {
        yield return new Module1();
        yield return new Module2();
    }
}

将该类添加到编译单元后,我们可以调用它并在运行时获取模块的静态列表,而不必搜索所有附加的程序集。我们本质上是在卸载编译器而不是运行时的工作。

鉴于我们可以通过以下方式访问编译的所有参考文献References属性,我看不到如何获取任何有用的信息,例如可能访问字节代码,可能加载用于反射的程序集,或类似的信息。

想法?


想法?

Yes.

通常,在模块环境中,您希望根据上下文或(如果适用)从第三方动态加载模块。相比之下,使用 Roslyn 编译器框架,您基本上可以在编译时获取此信息,从而将模块限制为静态引用。

就在昨天,我发布了动态加载工厂的代码。属性、加载 DLL 的更新等:GoF Factory 的命名约定? https://stackoverflow.com/questions/31263041/naming-convention-for-gof-factory?noredirect=1#comment50527877_31263041。据我了解,这与您想要实现的目标非常相似。该方法的优点是您可以在运行时动态加载新的 DLL。如果你尝试一下,你会发现它相当快。

您还可以进一步限制您处理的程序集。例如,如果您不处理mscorlib and System.*(或者甚至可能是所有 GAC 组件)当然它的工作速度会快得多。不过,正如我所说,这不应该是一个问题;仅扫描类型和属性是一个相当快的过程。


好的,更多信息和背景。

现在,您可能只是在寻找有趣的谜题。我能理解,玩弄技术毕竟很有趣。下面的答案(马修本人)将为您提供您需要的所有信息。

如果您想平衡编译时代码生成与运行时解决方案的优缺点,这里有来自我的经验的更多信息。

几年前,我认为拥有自己的 C# 解析器/生成器框架来执行 AST 转换是个好主意。这与您对 Roslyn 所做的非常相似;基本上,它将整个项目转换为 AST 树,然后您可以对其进行规范化、生成代码、对面向方面的编程内容进行额外检查并添加新的语言结构。我最初的目标是在 C# 中添加对面向方面编程的支持,为此我有一些实际应用。我不会向您介绍细节,但对于这种情况,足以说基于代码生成的模块/工厂也是我尝试过的事情之一。

性能、灵活性和代码量(在非库解决方案中)是我在运行时和编译时决策之间权衡决策的关键方面。让我们把它们分解一下:

  • 表现。这很重要,因为我不能假设库代码不在关键路径上。每个 appdomain 实例的运行时间将花费几毫秒。 (有关如何/为什么的说明,请参阅下文)。
  • 灵活性。它们在属性/扫描方面都具有同样的灵活性。但是,在运行时,您有更多更改规则的可能性(例如动态插入模块等)。我有时会使用它,特别是基于配置,这样我就不必在同一个解决方案中开发所有内容(因为那样效率低下)。
  • 代码量。根据经验,更少的代码通常是更好的代码。如果你做得正确,两者都会产生你在类上需要的单个属性。换句话说,两种解决方案在这里给出相同的结果。

不过,关于性能的说明是有必要的。我在代码中使用反射不仅仅是工厂模式。我基本上有一个广泛的“工具”库,其中包括所有设计模式(以及大量其他内容)。举几个例子:我在运行时自动为工厂、责任链、装饰器、模拟、缓存/代理(等等)等内容生成代码。其中一些已经要求我扫描程序集。

作为一个简单的经验法则,我总是使用属性来表示必须更改某些内容。您可以利用这一点来发挥您的优势:通过简单地将每种类型的属性(正确的程序集/命名空间)存储在单例/字典中的某个位置,您可以使应用程序更快(因为您只需要扫描一次)。从 Microsoft 扫描程序集也不是很有用。我对大型项目做了很多测试,发现在最坏的情况下,扫描使应用程序的启动时间增加了大约 10 毫秒。请注意,每次应用程序域实例化仅执行一次,这意味着您甚至不会注意到它。

类型的激活实际上是您将得到的唯一“真正的”性能损失。可以通过发出 IL 代码来优化该损失;这真的没那么难。最终的结果是,这里不会有任何区别。

总结一下,这是我的结论:

  • 表现: 差别不大。
  • 灵活性:运行时获胜。
  • 代码量: 差别不大。

根据我的经验,尽管许多框架希望支持即插即用架构,这可以从程序集中的删除中受益,但现实是,并没有大量的用例真正适用。

如果它不适用,您可能首先要考虑不使用工厂模式。另外,如果它适用的话,我已经证明它没有真正的缺点,那就是:如果你正确地实现它。不幸的是,我必须承认,我已经看到了很多糟糕的实现。

至于它实际上并不适用,我认为这只是部分正确。插入式数据提供者很常见(逻辑上它遵循 3 层架构)。我还使用工厂来连接诸如通信/WCF API、缓存提供程序和装饰器之类的东西(逻辑上遵循 n 层架构)。一般来说,它可用于您能想到的任何类型的提供商。

如果争论是它会带来性能损失,那么您基本上希望删除整个类型扫描过程。就我个人而言,我将其用于很多不同的事情,最显着的是缓存、统计、日志记录和配置。另外,我认为性能下降可以忽略不计。

只是我的2分钱; HTH。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Roslyn 获取引用程序集中的接口实现 的相关文章

  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • C#.Net 邮件将进入垃圾邮件文件夹

    我正在从 ASP net Web 应用程序发送电子邮件 邮件发送成功 没有失败 但大多数都进入了垃圾邮件文件夹 请帮助我克服垃圾邮件过滤器 我的发送邮件代码 public void SendMail string FromAddress s
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • 为什么 C 程序使用 Scanf 给出奇怪的输出?

    我目前正在学习 C 编程 并且遇到了这个奇怪的输出 Program will try functionalities of the scanf function include
  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • strlen() 编译时优化

    前几天我发现你可以找到编译时strlen使用这样的东西 template
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 防止控制台应用程序中的内存工作集最小化?

    我想防止控制台应用程序中的内存工作集最小化 在Windows应用程序中 我可以这样做覆盖 SC MINIMIZE 消息 http support microsoft com kb 293215 en us fr 1 但是 如何在控制台应用程
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • JavaScript 错误:MVC2 视图中的条件编译已关闭

    我试图在 MVC2 视图页面中单击时调用 JavaScript 函数 a href Select a JavaScript 函数 function SelectBenefit id code alert id alert code 这里 b
  • LinkLabel 无下划线 - Compact Framework

    我正在使用 Microsoft Compact Framework 开发 Windows CE 应用程序 我必须使用 LinkLabel 它必须是白色且没有下划线 因此 在设计器中 我将字体颜色修改为白色 并在字体对话框中取消选中 下划线
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • 如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView?

    我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表 MousePos 是一个自定义类 它保存鼠标 X Y 坐标 类型为 Point 和该位置的运行计数 我有一个线程 System Timers Timer
  • SQLAPI++ 的免费替代品? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何免费 也许是开源 的替代品SQLAPI http www sqlapi com 这个库看起来
  • C:设置变量范围内所有位的最有效方法

    让我们来int举个例子 int SetBitWithinRange const unsigned from const unsigned to To be implemented SetBitWithinRange应该返回一个int其中所有
  • 如何组合两个 lambda [重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中组合两个 lambda 表达式 https stackoverflow com questions 1717444 combining two lamba expressions in c
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • winform c# 中的弹出窗口

    我正在开发一个需要弹出窗口的项目 但问题是我还希望能够通过表单设计器在此弹出窗口中添加文本框等 所以基本上我有一个按钮 当您单击它时 它将打开我在表单设计器中设计的另一个窗口 我一直在谷歌搜索 但还没有找到我需要的东西 所以我希望你们能帮助
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配
  • ContentDialog Windows 10 Mobile XAML - 全屏 - 填充

    我在项目中放置了一个 ContentDialog 用于 Windows 10 上的登录弹出窗口 当我在移动设备上运行此项目时 ContentDialog 未全屏显示 并且该元素周围有最小的填充 在键盘上可见 例如在焦点元素文本框上 键盘和内

随机推荐

  • 截取 BlackBerry 中当前屏幕的屏幕截图

    是否可以在 BlackBerry 应用程序中截取当前屏幕的屏幕截图 我的屏幕上有一个按钮 当我单击该按钮时 我想发送一封包含当前屏幕截图的电子邮件 您可以使用以下方法获取显示屏幕的屏幕截图 public static Bitmap getS
  • mvc 3会话和authorizeAttribute

    我的网站对所有人开放 但我有一个控制器 其中有某种方法 只有具有用户名和密码的管理员才能输入 我正在拯救布尔IsManager in a session 我想使用授权属性来阻止任何人IsManager false 首先定义一个ActionF
  • 以编程方式导航时,Angular Router 旧组件保留在 dom 中

    角度版本 4 从登录后LoginComponent 位于public signin路线 我想导航到TestComponent在路径中public test 我这样做如下 this router navigate public test 然后
  • CSS - 三个可滚动等高主要内容列(流体高度)和粘性/始终可见的页脚

    我几乎浏览了这里和 Google 上的所有帖子 似乎找不到关于 3 个等高列 带有 真正 粘性页脚 的 CSS 布局问题的答案 布局要求如下 顶部标题部分 其中一行用于下拉菜单选项 另一行用于工具栏按钮 主要内容部分 有 3 个等高的列 中
  • Javascript 相当于 Python 的 locals() 吗?

    在Python中 我们可以使用内置函数获取当前范围内所有局部和全局变量的字典locals and globals 在 Javascript 中是否有一些等效的方法可以做到这一点 例如 我想做如下的事情 var foo function al
  • 使用值和引用参数类型重载的方法

    我有以下代码 class Calculator public int Sum int x int y return x y public int Sum out int x out int y x y 10 return x y class
  • d3:画笔更改单击鼠标悬停

    我正在尝试结合画笔选择 工具提示和单击事件来创建散点图 但似乎一旦我将画笔添加到 svg 画布 对象上的所有单击事件都会映射到鼠标悬停 有没有办法解决 下面的示例代码和 http jsfiddle net 7j8cr http jsfidd
  • 端口 7071 不可用。关闭使用该端口的进程,或使用 --port [-p] 指定另一个端口

    我尝试从本地运行 azure 函数应用程序 Http Triggerd API 使用 VS 代码 但我收到错误 端口 7071 不可用 使用该端口关闭进程 或使用 port p 指定另一个端口 我使用cmd提示符检查了使用的端口列表 但是7
  • 如何将 NSUInteger 转换为 NSString?

    你如何转换NSUInteger进入一个NSString 我已经尝试过但是我的NSString一直返回0 NSUInteger NamesCategoriesNSArrayCount self NamesCategoriesNSArray c
  • 从命令提示符或 PowerShell 调用 MSYS2 Shell

    从命令提示符或 PowerShell 开始 两者都可以 我可以弄清楚如何执行另一个给定的操作 如何调用 MSYS2 shell 来运行命令 更具体的问题 我包含此信息是为了避免 XY 问题 但我坚信最好通过回答上述 更一般的 问题来解决这个
  • 外部函数调用不同的包注意

    我正在准备一个 R 包以提交给 CRAN R CMD 检查给了我以下注释 外部函数调用不同的包 Fortran cinc 包 cmprsk Fortran crstm PACKAGE cmprsk 看 Writing R 的 系统和外语界面
  • 从 C# LINQ 解析 XML 时如何保留空白字符

    我需要在 C 代码或 XML 文档中做什么 以便 XDocument 解析器读取文字空白Values of XElements Background 我有一个 XML 文档 其中一部分如下所示
  • $settings 数组或 Config 类来存储项目设置?

    我应该如何存储项目设置 哪个更好 使用 settings包含我所有设置的数组 settings max photos 30 或创建一个单例Config类中包含所有设置 Class Config private max photos 30 有
  • linux top 显示java线程?

    在Linux服务器 fedora 中 我们运行单个JBOSS应用程序服务器 并使用quartz来调度我们的任务 昨天 我使用 top 命令来查看进程状态 查看多个名为Java显示不同的pid 但是如果我使用 ps aux grep java
  • 在滚动条到达底部之前执行 100px 的操作

    我有下面的 Javascript 当滚动条到达页面底部时 警报就会按预期显示 但是 我希望这发生在 100 像素处before它到达底部 我该怎么做呢 window scroll function if window scrollTop d
  • Glide:如何查找图像是否已缓存并使用缓存版本?

    Scenario 我有一个大的 GIF 图像 我想在用户第一次使用它打开应用程序时缓存该图像Glide https github com bumptech glide 图像加载和缓存库 之后 每当用户打开应用程序时 我想显示缓存的版本 如果
  • 核心数据:如何将自定义对象存储为可转换属性?

    目前我有一个名为 Place 的类 定义如下 class Place let name String let address String let coordinate CLLocationCoordinate2D let type Str
  • 使用 jquery 将解析后的文本转换为整数

    我当前正在解析一个 XML 文件 其中一个字段是一个整数 用于确定对象的大小 xml find Boxes each function var top box find top text 然后用 HTML 显示它 var html div
  • 使用增量主键sql插入多行

    INSERT INTO TABLE1 COLUMN1 PRIMARY KEY SELECT COLUMN1 SELECT COALESCE MAX PRIMARY KEY 0 FROM TABLE1 1 FROM TABLE2 error
  • 使用 Roslyn 获取引用程序集中的接口实现

    我想在我正在开发的框架中绕过一些经典的程序集扫描技术 因此 假设我定义了以下合同 public interface IModule 这存在于说Contracts dll 现在 如果我想发现该接口的所有实现 我们可能会执行类似于以下的操作 p