RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 607803
Accepted
iluxa1810
iluxa1810
Asked:2020-12-26 04:15:37 +0000 UTC2020-12-26 04:15:37 +0000 UTC 2020-12-26 04:15:37 +0000 UTC

System.Reflection.Emit 命名空间什么时候可以派上用场?

  • 772

System.Reflection.Emit 命名空间什么时候可以派上用场?

.NET 似乎提供了允许您以高级语言动态生成程序集的类。

为什么要低潜?

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Sergey Teplyakov
    2020-12-27T04:52:52Z2020-12-27T04:52:52Z

    正如上一个答案中已经提到的,有几种生成代码的方法。其中一些允许您在设计时生成代码,即 在 IDE (T4) 中。另一种是在运行时生成 C# 代码(CodeDom),第三种是在运行时生成可执行代码(即 IL)(Reflection.Emit)。

    尽管后一种情况似乎很少见,但事实并非如此。想都没想,你就一直在使用这项业务。

    编译正则表达式

    是的,如果你像这样创建一个正则表达式var regex = new Regex("abc", RegexOptions.Compiled);,那么运行时将使用 Reflection.Emit 生成一个带有自定义状态机的类,而不是每次都解释正则表达式。

    序列化器

    是的,大多数序列化程序都使用反射,但它非常慢。因此,同样的XmlSerializer会在运行时生成一个特殊的序列化器类,并存放在一个特殊的文件夹中,以备后用。二进制序列化程序使用相同的方法,而人人喜爱的 Newtonsoft.Json 并不轻视代码生成。

    PostSharp 和其他 AOP

    例如,我们想为日志记录创建一个运行时装饰器。如果我们有一个标有 attribute 的虚方法[LogMethod],那么我们可以生成一个继承类,它将覆盖这个虚方法并记录该方法的所有参数,然后才调用基实现。

    Postsharp 和其他 AOP 框架直接建立在运行时代码生成技术之上。

    表达式树

    是的,C# 中的表达式树是一种高级构造,但该方法Expression.Compile实际上使用 Reflection.Emit 在运行时生成代码。

    对象到对象映射器

    所有 Automappers 使用 Reflection.Emit 生成一种类型实例到另一种类型实例之间的映射。

    对象关系管理

    这是映射器的一个特例,它们充分利用了代码生成。NHibernate、LINQ 2 SQL、Entity Fraework 都在运行时生成类来将对象相互映射或拦截存储库方法调用。

    模拟框架

    Rhyno Mocks、Moq 等都基于表达式树或直接基于 Reflection.Emit(是的,许多模拟框架使用 DynamicProxy,它已经使用 Reflection.Emit)在测试时生成存根。

    WCF 和其他通讯工具

    是的,它们还为调用检查和其他运行时检测生成各种代理类。

    Application Insights 和其他记录器/分析器

    同样,为了使日志记录过程尽可能简单,此类工具中使用了代码生成。

    DLR - 动态语言运行时

    CLR 具有与动态类型语言(如 Python、Ruby 等)交互的特殊功能。在那里,DLR 被充分利用以提高性能。

    信不信由你,您甚至可以使用 Reflection.Emit(尽管是间接的)来解决标准 C# 问题。

    您可能知道new T()仿制药的局限性在于恐惧和恐怖。它使用反射,严重影响性能,甚至将实例化过程中发生的异常包装在TargetInvocationException. 我已经在几个项目中遇到过这种方法会显着破坏端到端的执行时间。因此,在这些项目中,使用了一个专门的项目CustomActivactor:

    public static class CustomActivator
    {
        public static T CreateInstance<T>() where T : new()
        {
            return ActivatorImpl<T>.Factory();
        }
    
        private class ActivatorImpl<T> where T : new()
        {
            // Под колпаком используем Reflection.Emit!
            private static readonly 
                System.Linq.Expressions.Expression<Func<T>> _expr = () => new T();
            public static readonly Func<T> Factory = _expr.Compile();
        }
    }
    

    现在我们的激活器不会遇到性能问题,也不会在 nerdy 中包装构造函数异常TargetInvocationExpression。

    Z.Y. 事实上,我只触及了部分脚本,同样的 ASP.NET 充分利用了代码生成。想要了解图片的丰满度,可以自己尝试搜索一下。例如,您可以从 System.Reflection.Emit 命名空间中查找ILGenerator类型或其他类型:

    • 在github上:找到了40K个链接ILGenerator(没有语义搜索,那么多链接不会有效,但会清楚悲剧的规模)。
    • 在 MS 中:许多链接和使用示例,其中大部分是我之前给出的。

    更新:这个主题已经变成了一篇关于激活器、泛型和代码生成的复杂性的相当长的帖子——剖析 C# 中的 new() 约束:泄漏抽象的完美示例。

    • 18
  2. Best Answer
    Vadim Ovchinnikov
    2020-12-26T14:16:49Z2020-12-26T14:16:49Z

    Reflection.Emit- 这适用于那些想要在纯 IL(简称硬核)上创建动态程序集(而不是向现有程序集添加内容)的人。更多细节如下。

    或者,也许您需要一些不同的东西,最好是更简单的东西?

    • 如果你只想要一个动态类型,也许就好了dynamic或者ExpandoObject?而且没有代码生成,您可以忘记在编译阶段进行检查。

    • 您是否需要一种方法(也可能是其中的一组)来接受和解析 lambda 表达式?然后使用表达式树。对于简单的情况,它会很好,但是如果您决定编写自己的 ORM,那么这个任务一点也不容易。但是请记住,表达式树仅支持单行 lambda 表达式。

    • 如果您需要动态生成 HTML(突然),并且这不是 Web 应用程序,则使用RazorEngine。您还可以通过模拟 Web 应用程序来添加 Intellisense 支持。


    代码生成

    简而言之:如果您仍然需要完整的代码生成,请使用 Roslyn。因为它功能强大且平台无关。T4 也可能有用(尤其是当您需要非 C# 和非 VB 代码时)。

    的优点和缺点Reflection.Emit

    优点

    • 在纯 IL 上创建动态程序集:您可以完全控制代码并可以编写高度优化的代码。这样做的好处是,如果某些功能在 C# 中不可用,但在 CLR 中可用,您也可以使用它。
    • 良好的代码生成性能。您创建的代码会立即编译成程序集(与 不同Microsoft.CodeDom,但 Roslyn 一切都很好,因为它将编译器作为服务拉取)。
    • 平台无关的方法(不像Microsoft.CodeDom,它只特定于 Microsoft 实现,但 Roslyn 也是平台无关的)

    瑕疵

    • 您必须编写最简单的东西,例如循环(只是一系列转换)。你得往低层次想。因此,很难创建非平凡的程序。
    • 您必须生成一个新程序集。如果它适合你,那么这不是缺点。但是,如果您想向现有的添加一些内容,那么使用Reflection.Emit.

    基于此,决定是否使用它Reflection.Emit。但我想这很硬核。

    备择方案

    • Microsoft.CodeDom
    • 罗斯林
    • T4

    Microsoft.CodeDom相对Reflection.Emit

    编程方法

    生成的带有帮助的代码Microsoft.CodeDom可以是解决方案的一部分(LINQ to SQL、WSDL、XSD 类是通过这种方式生成的)。您还可以使用partial类来自定义生成的代码。使用时,Reflection.Emit您需要创建一个新程序集。

    当您使用时,Reflecion.Emit您是在使用 IL 命令进行编程。当您使用Microsoft.CodeDomC# 编程时,您创建了 C# 命令。

    因此,代码Microsoft.CodeDom将更容易创建,因为您正在使用更高级别的命令进行编程。在 IL 中创建的代码Reflecion.Emit将更难在 IL 中创建(例如,需要使用转换创建循环),尤其是在编写复杂逻辑时。

    由此可以推断出Reflection.Emit更多的可能性(所以IL比C#有更多的可能性)。但是,对于 C# 中的每一个熟悉的东西,您将不得不编写更多的代码。

    表现

    代码Microsoft.CodeDom会比 慢Reflection.Emit,因为先编译,然后生成C#代码,再编译这个生成的代码。Reflection.Emit立即编译。

    平台(独立)依赖

    正如@Grundy 正确指出的那样,这对于Reflecion.Emit. 而且Microsoft.CodeDom它只能在 Microsoft 的平台上使用。

    罗斯林(基于问题)

    但是,我不会使用Microsoft.CodeDom. 因为 Roslyn 更强大,表现力更强。

    • Roslyn 在 Mono(独立于平台)上可用。
    • Roslyn 与Microsoft.CodeDomcsc.exe 不同,它不仅仅是对 csc.exe 的包装,还可以对代码进行语义分析,动态编译和解析代码。这是为 C# 和 VB 创建任何工具的方法。因此,有可能从格式化代码到在解决方案中查找给定符号的所有引用。

    T4

    但是,如果您不需要动态生成的程序集,而是需要补充现有解决方案的一部分怎么办?

    或者如果您需要动态生成的文件,而不是 C# 或 VB?例如,假设是 JavaScript 或 CSS。(对于 HTML,如果有的话,我推荐 RazorEngine,但使用 T4 也可以)。或者甚至是一些异国情调的扩展。然而,使任务变得更糟的是,需要其中的几个文件,但数量只有在代码执行时才知道。

    然后就可以使用T4了。它是 Visual Studio 中的一个工具(或只是一个控制台实用程序),用于生成任何语言的代码。在 MonoDevelop 中也可用。

    我必须马上说 Entity Framework(不是 .NET Core)使用 T4。

    概念上类似于 ASPX,只是 C# 插入之间的代码<# #>不一定是 HTML/CSS/JavaScript,而是目标文件的编程语言。手册。

    的缺点

    • Visual Studio 中没有内置的语法高亮和 Intellisense 支持,因此您要么习惯它,要么为 Visual Studio 安装第三方插件。
    • 12
  3. MSDN.WhiteKnight
    2022-03-24T14:14:58Z2022-03-24T14:14:58Z

    现有的答案有些过时。除了它们之外,需要注意的是,在 .NET Core 和 .NET 5+ 中,System.Reflection.Emit (SRE) 的功能与 .NET Framework / Mono 相比是有限的:您可以在内存中生成动态程序集,但是您不能将它们保存到可执行文件中。这意味着如果有人想在他们的编译器中使用 SRE 作为代码生成的基础,这个想法将不得不在今天的 .NET 环境中被放弃。这不会干扰使用 SRE 生成序列化器/映射器,除了现在动态程序集不能在不同的程序启动之间缓存——它必须总是重新生成,即使是相同的类型。如果需要将动态程序集保存到文件中,您也可以使用Mono.Cecil或系统.反射.元数据。

    此外,C# 最近还引入了动态代码生成的新功能,Source generators。在一些平时使用 SRE/CodeDom/T4 的情况下,现在可以尝试一下了。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5