大家都知道,在 dotnet 里的 Debug 下和 Release 下的一个最大的不同是在 Release 下开启了代码优化。启用代码优化,将会对生成的 IL 代码进行优化,同时优化后的 IL 也会有一些运行时的更多优化。内联是一个非常常用的优化手段,内联将会让 StackTrace 获取的调用堆栈存在 Debug 下和 Release 下的差异,从而导致获取方法标记的 Attribute 特性不能符合预期工作

这一个坑是来源于我所在团队开源的 CUnit (中文单元测试框架) 仓库的一次单元测试过程,我发现了在 Debug 下能通过测试,但是在 Release 下失败。详细请看: https://github.com/dotnet-campus/CUnit/actions/runs/3327386251/jobs/5502313113

核心原因是在 CUnit (中文单元测试框架) 仓库里使用了 StackTrace 的方式获取调用堆栈,通过调用堆栈获取各个方法,找到标记了 TestMethodAttribute 的方法,定位到标记是单元测试的方法

在 Release 下,发现找不到任何一个标记了 TestMethodAttribute 的方法。通过日志输出可以看到在 Debug 下和 Release 下的调用堆栈是不相同的。在 Release 下少了几个方法,刚好这几个方法里面就包含了一个标记了 TestMethodAttribute 的方法

其原因是在 Release 下默认开启了代码优化,在代码优化时,将会尝试内联一些函数,导致了调用堆栈中存在一些函数是看不到的,因为这些函数在实际运行过程中是不存在的,被内联到其他方法里面去了。换句话说,即使不是在 Release 下,只要开启了代码优化,那么都可能因为代码优化让某些函数被内联,从而让调用堆栈看起来不符合预期

因此,使用 StackTrace 获取调用堆栈,将在不同的环境下可能存在一些差异,导致逻辑不符合预期。如果再需要从方法上,获取方法标记的特性,那这个逻辑自然是不靠谱的

规避方法有两个:

第一个,那就是不要这么使用,找找其他的方法

第二个是,如果没有其他的方法,那可以考虑在明确需要获取某个特性的函数上,标记 [MethodImpl(MethodImplOptions.NoInlining)] 表示此函数不要被内联

dotnet 警惕使用 StackTrace 加获取方法标记 Attribute 特性在 Release 下被内联的更多相关文章

  1. PHP 加解密方法大全

    最近看见一篇文章讲的是PHP的加解密方法,正好也自己学习下,顺便以后有用到的地方也好能快速用上,仅供自己学习和复习,好了不多BB,上代码. 基于这几个函数可逆转的加密为:base64_encode() ...

  2. Linux系统的命令源码的获取方法

    我们知道,Linux是开源的,它自带的功能强大的命令也是开源的,也就是说.我们能够获得这些命令的源码并研究它.那到底怎样获得系统的命令的源码呢? 命令的源码是一个软件包为单元的,放在一个软件包的源码中 ...

  3. Java方法内联

    一.概念 方法内联就是把调用方函数代码"复制"到调用方函数中,减少因函数调用开销的技术   函数调用过程 1.首先会有个执行栈,存储它们的局部变量.方法名.动态连接 2.当一个方法 ...

  4. Java 方法内联

    什么是Java 方法内联? 我们先来看看普遍的内联函数含义.在维基百科中解释为: 内联函数:在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函 ...

  5. 仅反射加载(ReflectionOnlyLoadFrom)的 .NET 程序集,如何反射获取它的 Attribute 元数据呢?

    原文:仅反射加载(ReflectionOnlyLoadFrom)的 .NET 程序集,如何反射获取它的 Attribute 元数据呢? 平时我们获取一个程序集或者类型的 Attribute 是非常轻松 ...

  6. 先说一下JS的获取方法,其要比JQUERY的方法麻烦很多,后面以JQUERY的方法作对比。

    先说一下JS的获取方法,其要比JQUERY的方法麻烦很多,后面以JQUERY的方法作对比. JS的方法会比JQUERY麻烦很多,主要则是因为FF浏览器,FF浏览器会把你的换行也当最DOM元素 复制代码 ...

  7. <经验杂谈>C#中一种最简单、最基本的反射(Reflection):通过反射获取方法函数

    说起反射之前和很多用C#/.net的同仁们一样,相比于一般应用层对数据的增删改查总有点觉得深奥到难以理解.其实程序这东西,用过.实践过就很简单,我一直这么认为. 先说下概念:反射 Reflection ...

  8. c#图像处理入门(-bitmap类和图像像素值获取方法)

    c#图像处理入门 -bitmap类和图像像素值获取方法 一.Bitmap类 Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义 ...

  9. java 反射机制之 getDeclaredMethod()获取方法,然后invoke执行实例对应的方法

    关于反射中getDeclaredMethod().invoke()的学习,来源于项目中的一行代码: SubjectService.class.getDeclaredMethod(autoMatchCo ...

  10. 【转】StackTraceElement获取方法调用栈的信息

    本文链接:https://blog.csdn.net/hp910315/article/details/52702199 一.什么是StackTrace StackTrace(堆栈轨迹)存放的就是方法 ...

随机推荐

  1. 【Leetcode】63. 不同路径 II

    题目(链接) 一个机器人位于一个m x n网格的左上角 (起始点在下图中标记为 "Start" ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为 ...

  2. 记录--一行代码修复100vh bug

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 你知道奇怪的移动视口错误(也称为100vh bug)吗?或者如何以正确的方式创建全屏块? 一.100vh bug 什么是移动视口错误? 你 ...

  3. 《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南 - 第9章

    本章勘误: 暂无,等待细心的你告诉我哦. 本章注解: 暂无 本章释疑: 暂无,等待你的提问 致谢: MVP 林德熙 MVP 吕毅 sPhinX 相关链接 试读记录

  4. 并发CPU伪共享及优化

    伪共享 缓存系统中是以缓存行(cache line)为单位存储的.缓存行是2的整数幂个连续字节,一般为32-256个字节.最常见的缓存行大小是64个字节.当多线程修改互相独立的变量时,如果这些变量共享 ...

  5. KafkaConsumerDemo

    pom <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>sp ...

  6. C++中结构体的各种用法,详细版

    C++中的结构体是一种用户自定义的数据类型,可以存储不同类型的数据成员.以下是一些结构体的常见用法: 1. 定义结构体类型 结构体类型的定义可以放在函数内部或外部,例如: ``` struct Per ...

  7. Scala 元祖Tuple

    1 package chapter07 2 3 object Test10_Tuple { 4 def main(args: Array[String]): Unit = { 5 // 1. 创建元组 ...

  8. clang的lto特性的资料

    clang对lto的支持,如下文章介绍的清晰.易懂. ThinLTO llvm+clang 添加 LTO(Link Time Optimization) 支持 编译优化之 - 链接时优化(LTO)入门 ...

  9. SpringCloud OpenFeign token中转

    OpenFeign 的本质是调用指定服务的 Controller. 前后端分离项目,调用 Controller 是需要传递 token 的. OpenFeign 并不会自动携带 token 去访问 C ...

  10. MogDB学习笔记之 -- 了解pagewriter线程

    MogDB 学习笔记之 -- 了解 pagewriter 线程 本文出处:https://www.modb.pro/db/183172 在前面的 MogDB 学习系列中,我们了解了核心的 bgwrit ...