如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下
我们经常会使用条件编译符 #if DEBUG 在 Debug 下执行某些特殊代码。但是一旦我们把代码打包成 dll,然后发布给其他小伙伴使用的时候,这样的判断就失效了,因为发布的库是 Release 配置的;那些 #if DEBUG 的代码根本都不会编译进库中。然而总有时候希望在库中也能得知程序是 Debug 还是 Release,以便库发布之后也能在 Debug 下多做一些检查。
那么有办法得知使用此库的程序是 Debug 配置还是 Release 配置下编译的呢?本文将介绍一个比较靠谱的方法(适用于 .NET Standard)。
先上代码
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Walterlv.ComponentModel
{
/// <summary>
/// 包含在运行时判断编译器编译配置中调试信息相关的属性。
/// </summary>
public static class DebuggingProperties
{
/// <summary>
/// 检查当前正在运行的主程序是否是在 Debug 配置下编译生成的。
/// </summary>
public static bool IsDebug
{
get
{
if (_isDebugMode == null)
{
var assembly = Assembly.GetEntryAssembly();
if (assembly == null)
{
// 由于调用 GetFrames 的 StackTrace 实例没有跳过任何帧,所以 GetFrames() 一定不为 null。
assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
}
var debuggableAttribute = assembly.GetCustomAttribute<DebuggableAttribute>();
_isDebugMode = debuggableAttribute.DebuggingFlags
.HasFlag(DebuggableAttribute.DebuggingModes.EnableEditAndContinue);
}
return _isDebugMode.Value;
}
}
private static bool? _isDebugMode;
}
}
再解释原理
发现特性
所有 .NET 开发者都应该知道我们编译程序时有 Debug 配置和 Release 配置,具体来说是项目文件中一个名为 <Configuration> 的节点记录的字符串。
使用 Debug 编译后的程序和 Release 相比有哪些可以检测到的不同呢?我反编译了我的一个程序集。
.NET Core 程序集,Debug 编译:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyCompany("Walterlv.Demo")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Walterlv.Demo")]
[assembly: AssemblyTitle("Walterlv.Demo")]
.NET Core 程序集,Release 编译:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Walterlv.Demo")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Walterlv.Demo")]
[assembly: AssemblyTitle("Walterlv.Demo")]
发现一个很棒的特性 AssemblyConfiguration,直接写明了当前是 Debug 还是 Release 编译的。
你以为这就完成了?我们再来看看 .NET Framework 下面的情况。
.NET Framework 程序集,Debug 编译:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Walterlv.Demo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Walterlv.Demo")]
[assembly: AssemblyCopyright("Copyright © walterlv 2018")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7", FrameworkDisplayName = ".NET Framework 4.7")]
.NET Framework 程序集,Release 编译:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Walterlv.Demo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Walterlv.Demo")]
[assembly: AssemblyCopyright("Copyright © walterlv 2018")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7", FrameworkDisplayName = ".NET Framework 4.7")]
已经没有 AssemblyConfiguration 特性可以用了。不过我们额外发现一个比较间接的特性可用 Debuggable,至少两者都是有的,可以写出兼容的代码。
DebuggableAttribute.DebuggingModes 有多个值:
None- 自 .NET Framework 2.0 开始,JIT 跟踪信息始终会生成,所以这个属性已经没用了。如果指定为这个值,会直接按
Default处理。
- 自 .NET Framework 2.0 开始,JIT 跟踪信息始终会生成,所以这个属性已经没用了。如果指定为这个值,会直接按
Default
- 允许 JIT 编译器进行优化。
DisableOptimizations
- 禁止编译器对输出程序集进行优化,因为优化可能导致调试过程非常困难。
IgnoreSymbolStoreSequencePointsEnableEditAndContinue
- 允许在进入断点的情况下编辑代码并继续执行。
通常在 Debug 下编译时,使用的值是 EnableEditAndContinue。
寻找程序集
以上发现的程序集特性是需要找到一个程序集的,那么应该使用哪一个程序集呢?
通常我们调试的时候是运行一个入口程序的,所以可以考虑使用 Assembly.GetEntryAssembly() 来获取入口程序集。然而微软官网对此方法有一个描述:
The assembly that is the process executable in the default application domain, or the first executable that was executed by AppDomain.ExecuteAssembly. Can return null when called from unmanaged code.
也就是说如果入口程序集是非托管程序集,那么这个可能返回 null。这可能发生在单元测试中、性能测试中或者其他非托管程序调用托管代码的情况;虽然不是主要场景,却很常见。所以,我们依然需要处理返回 null 的情况。
那么如何才能找到我们需要的入口程序集呢?考虑托管代码的调用栈中的第一个函数可能是最接近使用者调试的程序集的,所以我们可以采取查找栈底的方式:
var assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
StackTrace.GetFrames() 方法可能返回 null,但那仅对于一个任意的 StackTrace。在我们的使用场景中是取整个托管调用栈的,由于这个方法本身就是托管代码,所以栈中至少存在一个帧;也就是说此方法在我们的场景中是不可能返回 null 的。所以代码静态检查工具如果提示需要处理 null,其实是多余的担心。
性能
另外,一个编译好的程序集是不可能在运行时再去修改 Debug 和 Release 配置的,所以第一次获取完毕后就可以缓存下来以便后续使用。
参考资料
- Assembly.GetEntryAssembly Method (System.Reflection)
- c# - I need an alternative to
Assembly.GetEntryAssembly()that never returns null - Stack Overflow - StackTrace.GetFrames
如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下的更多相关文章
- python代码中判断版本
在python代码中判断python版本: if sys.version_info < (3, 0): lib.make_flows.argtypes = [c_char_p, c_char_p ...
- bat代码中判断 bat是否是以管理员权限运行,以及自动以管理员权限运行CMD BAT
· bat 代码中判断bat是否是以管理员权限运行,以及自动以管理员权限运行CMD BAT 一.判断bat是否是以管理员权限运行 @echo off net.exe session >NUL & ...
- 如何在C或C++代码中嵌入ARM汇编代码
转载自:http://blog.csdn.net/roland_sun/article/details/42921131 大家知道,用C或者C++等高级语言编写的程序,会被编译器编译成最终的机器指令. ...
- java中如何在代码中判断时间是否过了10秒
long previous = 0L; ... { Calendar c = Calendar.getInstance(); long now = c.getTimeInMillis(); //获取当 ...
- iOS 中判断应用程序是否为第一次打开
第一步:在AppDelegate中当应用启动完成后加入一下代码: - (BOOL)application:(UIApplication *)application didFinishLaunching ...
- [C#]中获取当前程序运行路径的方法
获取当前程序运行路径: ①//获取当前 Thread 的当前应用程序域的基目录,它由程序集冲突解决程序用来探测程序集.string str = System.AppDomain.CurrentDoma ...
- GA代码中的细节
GA-BLX交叉-Gaussion变异 中的代码细节: 我写了一个GA的代码,在2005测试函数上一直不能得到与实验室其他同学类似的数量级的结果.现在参考其他同学的代码,发现至少有如下问题: 1.在交 ...
- 6.python在windows下用批处理文件在运行中输入程序名直接运行的方法
最近由于平时自由时间比较多,在看一本python入门书籍,在里面学习了一种用windows下的批处理文件在电脑运行界面中直接输入程序名称就可运行的方法,现将其详细说明如下: 1.首先编写一个教程上的程 ...
- 如何:在 DHTML 代码和客户端应用程序代码之间实现双向通信
https://msdn.microsoft.com/zh-cn/library/a0746166 可以使用 WebBrowser 控件向 Windows 窗体客户端应用程序添加现有的动态 HTML ...
随机推荐
- [WCF安全1]使用basicHttpBinding构建UserName授权的WCF应用程序
最近到了新公司,leader让我研究一下WCF的传输安全机制.以前也做过WCF的应用,但是很少涉及安全方面的东西.所以,花了三天的时间研究了一下如何在WCF的应用程序中配置安全.在这个系列文章中,我会 ...
- Kaggle 项目之 Digit Recognizer
train.csv 和 test.csv 包含 1~9 的手写数字的灰度图片.每幅图片都是 28 个像素的高度和宽度,共 28*28=784 个像素点,每个像素值都在 0~255 之间. train. ...
- js 捕获型事件
true 为捕获型事件 false 为冒泡型事件
- JavaScript清除字符串前后空格
一.通过循环检查,然后提取非空格字符串 //去掉前后空白 function trim(s){ return trimRight(trimLeft(s)); } //去掉左边的空白 function t ...
- 【IIS转】:解决IIS下localhost访问需要输入用户名和密码的问题
[摘要]安装完IIS后,我们可能会发现浏览器输入localhost访问时提示我们输入用户名和密码,本文介绍了这种问题的解决方法,方便站长们调试自己的程序. 在我们的WIN XP系统中安装了IIS,但是 ...
- UVA-1623 Enter The Dragon (贪心)
题目大意:有n个装满水的湖,m天.每天可能下雨也可能晴天,只要下雨就会把湖填满,若已满,则发洪水.有一台只能在晴天使用的抽水机,每次抽水只能抽一个湖,并且全部抽光.问是否存在一种使得不发洪水的抽水方案 ...
- [转载]各种java生成word解决方案的优缺点对比
解决方案 优点 缺点 Jacob 功能强大 直接调用VBA接口,程序异常复杂:服务器必须是:windows系统+安装Office:服务器端自动化com接口容易产生死进程造成服务器宕机 Apache P ...
- Zabbix在CentOS7上的安装方法:
).zabbix-server 要安装zabbix-server首先需要安装MySQL数据库,当然你可以将MySQL换成其他的数据库 1.1)创建zabbix数据库:CREATE DATABASE z ...
- 重置input checked
<!-- 作者:duke 时间:2018-10-24 描述: 重置input 样式--> <!DOCTYPE HTML><html> <head> &l ...
- fegin--调用超时设置
Feign请求超时问题 Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码.而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间 ...