草庐IT

c# - 使用 Roslyn 在引用的程序集中获取接口(interface)实现

coder 2024-05-23 原文

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

所以,假设我定义了以下契约(Contract):

public interface IModule
{

}

这存在于说 Contracts.dll .

现在,如果我想发现这个接口(interface)的所有实现,我们可能会做类似以下的事情:
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 访问编译的所有引用。属性,我看不到如何获得任何有用的信息,例如可能访问字节码,可能加载程序集进行反射,或类似的信息。

    想法?

    最佳答案

    Thoughts?



    是的。

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

    就在昨天,我发布了动态加载工厂的代码。属性、加载 DLL 等的更新在这里:Naming convention for GoF Factory? .据我了解,它与您想要实现的目标非常相似。这种方法的好处是您可以在运行时动态加载新的 DLL。如果您尝试一下,您会发现它非常快。

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

    好的,更多信息和上下文。

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

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

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

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

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

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

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

    总结一下,这是我的结论:
  • 性能:差别不大。
  • 灵活性:运行时胜出。
  • 代码量:差别不大。

  • From my experience, although a lot of frameworks hope to support plug and play architectures which could benefit from drop in assemblies, the reality is, there isn't a whole load of use-cases where this is actually applicable.



    如果它不适用,您可能首先要考虑不使用工厂模式。此外,如果它适用,我已经表明它没有真正的缺点,即:如果您正确实现它。不幸的是,我必须在这里承认,我已经看到了很多糟糕的实现。

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

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

    只是我的 2 美分;哈。

    关于c# - 使用 Roslyn 在引用的程序集中获取接口(interface)实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31119284/

    有关c# - 使用 Roslyn 在引用的程序集中获取接口(interface)实现的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    5. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    7. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    随机推荐