通常情况下,出于多种原因,我不会说我喜欢写关于预览功能的文章。我的大多数帖子旨在帮助人们解决他们可能遇到的问题,而不是找个肥皂盒或打广告。但是我认为我应该介绍这个 .NET 预览特性,因为它是我在 .NET 生态系统中渴望已久的东西(猴子补丁,monkey patching,在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷,猴子补丁在代码运行时内存中发挥作用,不会修改源码,因此只对当前运行的程序实例有效;因为猴子补丁破坏了封装,而且容易导致程序与补丁代码的实现细节紧密耦合,所以被视为临时的变通方案,不是集成代码的推荐方式)的姊妹主题。如果你不熟悉这个话题,我建议你阅读我关于猴子打补丁的帖子。一般来说,猴子补丁允许你用一个实现代替另一个实现,你知道吗,. NET 8引入了拦截器的概念。

  顾名思义,拦截器允许开发人员针对特定的方法调用,用新的实现拦截它们。拦截器有几个目的和重要的区别,我们将在这篇文章中讨论。让我们开始吧。

拦截器是什么?

  在 .NET 8预览版6中,SDK 引入了额外的功能来“拦截”代码库中的任何方法调用。“interceptor(拦截器)”这个词很清楚地说明了这个新功能的目的。它只是有意地替换方法,而不是全局地替换方法实现。这种方法意味着,作为开发人员,您必须系统地使用拦截器。

  . NET 团队使用拦截器将以前依赖于反射的基础架构代码重写为特定于应用程序的编译时版本。拦截器有望减少程序的启动时间和提高效率。. NET 团队设计了拦截器来与源代码生成器(source generator)一起工作,因为源代码生成器可以处理抽象语法树和代码文件以实现目标方法调用。虽然您可以手动编写拦截器调用,但这在实际应用程序中是不切实际的。

  让我们开始设置您的项目以使用拦截器。

入门

  拦截器是 .NET 8预览版6的一个特性,所以你需要匹配其 SDK 版本或更高版本才能使用它。首先创建一个新的控制台应用程序,或者任何 .NET 应用程序。

  接下来,在 .csproj 中,必须添加以下 PropertyGroup 元素。

<PropertyGroup>
<Features>InterceptorsPreview</Features>
</PropertyGroup>

  还要确保将 LangVersion 元素设置为预览以访问该特性。

<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>

  接下来,将以下属性定义添加到项目中。

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int character)
{
}
}

  是的,这个属性不是 BCL 的一部分是很奇怪的,但由于这是一个预览特性,我想 .NET 团队不想在以后的 API 更改中污染 .NET 框架。

  您将注意到该属性有三个参数:filePath、line 和 character。您还会注意到,这些值没有在任何地方赋值,您是正确的。该属性是编译器将在编译时读取的标记,因此设置运行时使用的值是没有意义的。

  现在,让我们拦截一些代码。将以下内容添加到 Program.cs 文件中。注意,行号和间距非常重要。如果重新格式化代码,这个解决方案可能会失效。还要确保将文件路径更改为 Program.cs 文件的绝对路径。

using System.Runtime.CompilerServices;

C.M(); // What the Fudge?!
C.M(); // Original class C
{
public static void M() => Console.WriteLine("Original");
} // generated
class D
{
[InterceptsLocation("/Users/khalidabuhakmeh/RiderProjects/ConsoleApp12/ConsoleApp12/Program.cs",
line: 3, character: 3)]
public static void M() => Console.WriteLine("What the Fudge?!");
}

  运行上面的应用程序,您将看到最奇怪的事情。同一个方法调用的两个不同输出!搞什么鬼?

  如何做到的?编译后的代码是什么样子的?我们可以使用 JetBrains Rider 的 IL Viewer 看到发生了什么。

// Decompiled with JetBrains decompiler
// Type: Program
// Assembly: ConsoleApp12, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 09D7E1E0-5709-4A62-884A-AB84DAA1E08C
// Assembly location: /Users/khalidabuhakmeh/RiderProjects/ConsoleApp12/ConsoleApp12/bin/Debug/net8.0/ConsoleApp12.dll
// Local variable names from /users/khalidabuhakmeh/riderprojects/consoleapp12/consoleapp12/bin/debug/net8.0/consoleapp12.pdb
// Compiler-generated code is shown using System.Runtime.CompilerServices; [CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
D.M();
C.M();
} public Program()
{
base..ctor();
}
}

  现在可以看到,编译器用我们的拦截实现替换了第一个方法调用。哇!

  在这种令人眼花缭乱的感觉褪去之后,你可能会认为这是不切实际的。谁有时间硬编码文件的完整路径、计算行数和列数呢?正如前面提到的,这就是源代码生成器的用武之地。

  虽然在处理语法树时我不会在这里演示它,但是您确实可以访问如 FilePath 之类的信息,并且每个 CSharpSyntaxNode 都有一个 GetLocation 方法,该方法使您可以访问代码文件中的行号和位置。如果您已经精通编写源代码生成器,那么您已经可以获得这些信息。

结论

  这个特性是针对 .NET 社区中特定的一群人,特别是那些编写和维护源代码生成器的人。在这个小群体中,您可能会有框架作者希望从 .NET 中挤出最后一点性能。正如您所看到的,拦截器只能更改特定的实现,而不能全局地针对方法。如果使用源代码生成器对所有方法进行拦截,则必须为每个位置生成一个拦截调用。生成大量自定义代码可能会对编译资产的大小产生不利影响,因此要注意使用此特性。另外,您可以考虑完全避免这个功能。拦截器仍处于预览阶段,其主要目的是帮助 .NET 作者改进 ASP .NET Core 和 .NET SDK 中的其他框架。不管怎样,在下次调试 .NET 8应用程序时,了解这个特性是有好处的,因为你认为你调用的方法可能不是你实际调用的方法。

  我希望你喜欢这篇博文,并一如既往地感谢你阅读并与朋友和同事分享我的博文。

原文链接:https://khalidabuhakmeh.com/dotnet-8-interceptors

【译】.NET 8 拦截器(interceptor)的更多相关文章

  1. struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo

    理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...

  2. struts2拦截器interceptor的三种配置方法

    1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ...

  3. SSM-SpringMVC-33:SpringMVC中拦截器Interceptor讲解

     ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 拦截器Interceptor: 对处理方法进行双向的拦截,可以对其做日志记录等 我选择的是实现Handler ...

  4. 过滤器(Filter)和拦截器(Interceptor)

    过滤器(Filter) Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上,基于函数回调,它可以对几乎所有请求 ...

  5. 二十五、过滤器Filter,监听器Listener,拦截器Interceptor的区别

    1.Servlet:运行在服务器上可以动态生成web页面.servlet的声明周期从被装入到web服务器内存,到服务器关闭结束.一般启动web服务器时会加载servelt的实例进行装入,然后初始化工作 ...

  6. Flume 拦截器(interceptor)详解

    flume 拦截器(interceptor)1.flume拦截器介绍拦截器是简单的插件式组件,设置在source和channel之间.source接收到的事件event,在写入channel之前,拦截 ...

  7. struts2拦截器interceptor的配置方法及使用

    转: struts2拦截器interceptor的配置方法及使用 (2015-11-09 10:22:28) 转载▼ 标签: it 365 分类: Struts2  NormalText Code  ...

  8. Kafka producer拦截器(interceptor)

    Producer拦截器(interceptor)是个相当新的功能,它和consumer端interceptor是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑. 对于 ...

  9. Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行

    在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...

  10. JavaWeb—拦截器Interceptor

    1.概念 java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取A ...

随机推荐

  1. 详解JS的Object.create()

    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__. 语法 Object.create(proto[, propertiesObject]) Obj ...

  2. react中useRef的应用

    何为useRef useRef是随着react函数式组件发展而来的,是react众多官方hook中的一个,调用useRef可以返回一个伴随这组件整个声明周期不发生改变的对象,这个对象常见的用途有两个: ...

  3. FreeSWITCH添加自定义endpoint

    操作系统 :CentOS 7.6_x64      FreeSWITCH版本 :1.10.9   日常开发过程中会遇到需要扩展FreeSWITCH对接其它系统的情况,这里记录下编写FreeSWITCH ...

  4. C++程序开发技巧

    引言 类(class)的使用分为两种--基于对象(object Based)和面向对象(object oriented) 基于对象是指,程序设计中单一的类,和其他类没有任何关系 单一的类又分为:不带指 ...

  5. 【实战分享】使用 Go 重构流式日志网关

    项目背景 分享之前,先来简单介绍下该项目在流式日志处理链路中所处的位置. 流式日志网关的主要功能是提供 HTTP 接口,接收 CDN 边缘节点上报的各类日志(访问日志/报错日志/计费日志等),将日志作 ...

  6. 【城南】如何识别AI生成图?视觉AIGC伪造检测技术综述

    如何识别 AI 生成图片?or 如何识别 AIGC 图?or 如何识别 AI 换脸?or AI生成图伪造检测? 类似的说法有很多种,总之就是利用AI技术来鉴别一张图是不是AI生成的,这种AI技术就是本 ...

  7. 【HarmonyOS】详解低代码端云一体化开发之连接器

    ​[关键字] 元服务.低代码平台.端云一体化开发.连接器.拖拽式UI [1.写在前面] 前面我们写了两篇文章分别介绍了低代码平台的基本使用和端云一体化开发中数据模型的使用,有需要的可以了解一下,文章地 ...

  8. ELK日志收集记录

    logstash在需要收集日志的服务器里运行,将日志数据发送给es 在kibana页面查看es的数据 es和kibana安装: Install Elasticsearch with RPM | Ela ...

  9. Task Execution and Scheduling In SpringBoot

    开天辟地 Task Execution and Scheduling In the absence of an Executor bean in the context, Spring Boot au ...

  10. 西门子S7系列PLC以太网通讯处理器编程调试方法

    捷米特(北京)科技有限公司研发的捷米特以太网通讯模块,转以太网通讯模块型号有ETH-S7200-JM01和ETH-S7300-JM01,适用于西门子S7-200/S7-300/S7-400.SMART ...