C#調(diào)試Debug/Release入門篇
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
DotNet程序的調(diào)試,是DotNet程序員必備的技能之一,開(kāi)發(fā)出穩(wěn)定的程序、解決程序的疑難雜癥都需要很強(qiáng)大的調(diào)試能力。DotNet調(diào)試有很多方法和技巧?,F(xiàn)在本文就介紹一下借助DebugView工具進(jìn)行調(diào)試的方法,以及由DebugView引申出來(lái)的知識(shí)點(diǎn)。 DebugViewDebugView是一個(gè)查看調(diào)試信息的非常棒的工具,支持Debug、Release模式編譯的程序,甚至支持內(nèi)核程序,而且能夠定制各種過(guò)濾條件,讓你只看到關(guān)心的輸出信息,而且可以定制高亮顯示的內(nèi)容等等,非常方便。 捕捉Release模式的Win32程序輸出的調(diào)試信息,需要選中Capture Global Win32選項(xiàng): 過(guò)濾與高亮功能可以通過(guò)include、exclude設(shè)置過(guò)濾條件,包含指定字符串的輸出信息將會(huì)被過(guò)濾。還可以通過(guò)exclude條件過(guò)濾掉對(duì)應(yīng)進(jìn)程ID的調(diào)試信息。多個(gè)條件使用“;”分隔,而且支持“*”通配符。 遠(yuǎn)程調(diào)試DebugView支持遠(yuǎn)程捕捉調(diào)試信息。首先在遠(yuǎn)程機(jī)器上通過(guò)如下命令啟動(dòng)DebugView: DebugView.exe /a /t /g /s 這樣,DebugView就會(huì)以服務(wù)的方式運(yùn)行,如下圖: 然后在本地機(jī)器上啟動(dòng)DebugView,并通過(guò)Connect連接到遠(yuǎn)程機(jī)器的DebugView,當(dāng)遠(yuǎn)程機(jī)器中有調(diào)試信息輸出時(shí),本地就會(huì)捕獲到,并展示出來(lái): 輸出信息到DebugView的幾種方式DebugView的一些功能是不是讓你心動(dòng)了呢。俗話說(shuō)心動(dòng)不如行動(dòng),但是在行動(dòng)之前,首先要知道C#如何將調(diào)試信息輸出到DebugView中。 通過(guò)編程輸出一些調(diào)試信息到DebugView中,一共有三種方式:
一、Debug.WriteLine通過(guò)Debug.WriteLine可以將調(diào)試信息寫(xiě)入到DebugView中,如下: Debug.WriteLine("這是調(diào)試信息"); 效果如下: 不過(guò)此方式只能在Debug模式下有效。具體原因下面會(huì)詳細(xì)介紹。 二、Debugger.LogDebug.WriteLine已經(jīng)很好用了,唯一的缺點(diǎn)就是在Release模式下無(wú)效。那么在Release模式下就可以使用Debugger.Log方法,示例如下: Debugger.Log(0, null, "這是Debugger.Log輸出的調(diào)試信息"); 三、Kernel32.dll中的OutputDebugString方法做C++開(kāi)發(fā)的應(yīng)該知道可以通過(guò)OutputDebugString這個(gè)API開(kāi)實(shí)現(xiàn)輸出調(diào)試信息到DebugView中吧。那么C++能做的,C#也能做??梢酝ㄟ^(guò)PInvoke的方式引入此方法,這個(gè)API屬于Kernel32.dll,如下聲明: [DllImport("kernel32.dll", CharSet=CharSet.Auto)]public static extern void OutputDebugString(string message); 然后就可以通過(guò)調(diào)用此方法,將調(diào)試信息輸出到DebugView中。 DebugView與日志框架比較可能有人會(huì)說(shuō),DebugView能做的事情,我用log4Net,NLog等日志框架也能做,為什么要用DebugView呢? 問(wèn)的好,那么我就根據(jù)平時(shí)使用DebugView的經(jīng)驗(yàn)來(lái)給你一個(gè)用DebugView的理由:
這些理由應(yīng)該足以讓你使用DebugView了吧。使用DebugView的理由肯定還不止這些,如果你有更好的理由,還請(qǐng)分享出來(lái)。 當(dāng)然,DebugView與日志框架,每個(gè)都有每個(gè)的用途。通過(guò)DebugView的方式,只適合短暫的調(diào)試,而正式發(fā)布的網(wǎng)站或者軟件,需要一套記錄程序長(zhǎng)期以來(lái)的運(yùn)行狀態(tài)的工具,那么就非日志框架莫屬了。所以DebugView與日志框架,要在合適的地方,發(fā)揮他們最大的功效。 聲明Log4Net等日志框架,功能足夠強(qiáng)大,也足夠豐富,相信上面說(shuō)到的DebugView的功能,也可以通過(guò)日志框架來(lái)實(shí)現(xiàn)。但是和DebugView比較起來(lái),會(huì)相對(duì)復(fù)雜一些。所以上面說(shuō)到的使用DebugView的理由是基于方便性的比較,DebugView有足夠的方便性來(lái)讓你選擇使用他。 ConditionalAttribute詳解與條件編譯說(shuō)到調(diào)試,那么肯定有開(kāi)發(fā)人員遇到這種情況,開(kāi)發(fā)產(chǎn)品的時(shí)候,遇到一些問(wèn)題,就在代碼中加入了大量的調(diào)試輸出信息,比如通過(guò)Console.WriteLine、MessageBox.Show或者通過(guò)Ilog.Log記錄日志,甚至臨時(shí)改變邏輯來(lái)驗(yàn)證邏輯的正確性等。經(jīng)過(guò)這些調(diào)試信息的幫助,終于解決了產(chǎn)品的問(wèn)題。但此時(shí)又遇到了新的問(wèn)題,產(chǎn)品最終發(fā)布的時(shí)候,肯定是不能有這些調(diào)試信息的,可是已經(jīng)加了這么多調(diào)試信息,難道要全部刪除掉嗎。這顯然不是一個(gè)好辦法,這么多代碼,手一抖,很容易就刪除了不相關(guān)的代碼,造成不可預(yù)估的后果。 做過(guò)C/C++開(kāi)發(fā)的,可以從各種跨平臺(tái)的開(kāi)源庫(kù)中看到,一堆一堆的#if....#else....#endif,這就是條件編譯,這也是C/C++跨平臺(tái)所依賴的最基本的東西,在Linux系統(tǒng),編譯這段代碼,在Windows系統(tǒng)又編譯那段代碼,最終實(shí)現(xiàn)了代碼級(jí)別的跨平臺(tái)。 那么C#中有沒(méi)有類似的功能呢,答案當(dāng)然是有,而且有兩種:
ConditionalAttribute特性下面是ConditionalAttribute的構(gòu)造函數(shù): public ConditionalAttribute(string conditionString 構(gòu)造函數(shù)中的參數(shù)conditionString,是一個(gè)區(qū)分大小寫(xiě)的條件編譯符號(hào)的字符串。 上面提到Debug.WriteLine時(shí),說(shuō)到這個(gè)功能只在Debug模式下才有用,Release下就不起作用了。 我們從MSDN中看一下Debug.WriteLine的說(shuō)明: [ConditionalAttribute("DEBUG")]public static void WriteLine(string message 由此也就明白了Debug.WriteLine只能在Debug模式下使用,而在Release模式下無(wú)效的原因了。 條件編譯#if..#else..#endifC/C++中有#if..#else..#endif,C#中也有這些,他們都被稱為預(yù)處理器。通過(guò)預(yù)定義的條件編譯符號(hào),來(lái)控制編譯時(shí)編譯哪部分代碼。如下: public bool HasPermission(string userName) { #if DEBUG //Debug模式下,不需要做權(quán)限判斷,直接返回true return true; #else //Release模式下,只有sa用戶才有權(quán)限 if (!string.IsNullOrEmpty(userName) && userName == "sa") { return true; } else { return false; } #endif } 預(yù)定義的Debug宏在什么地方說(shuō)到條件編譯,是不是只有DEBUG 和 RELEASE兩種情況呢,如果是這種情況的話,那也就是說(shuō)DEBUG和RELEASE兩種情況是定義好了的,俗話說(shuō)就是“做死了”,這是作死的節(jié)奏啊。不作死就不會(huì)死,至少VS在這點(diǎn)上還沒(méi)有作死。 讓我們來(lái)一步步揭開(kāi)DEBUG的面紗。 既然是條件編譯,那么就應(yīng)該和編譯選項(xiàng)有關(guān)。我們知道C#項(xiàng)目,有一個(gè)屬性頁(yè),可以設(shè)置很多編譯的選項(xiàng),如下: 從圖中看到,條件編譯是用的DEBUG常量,或者稱為DEBUG條件編譯符號(hào),是在這個(gè)編譯生成選項(xiàng)中定義的,如果去掉這個(gè)定義,那么編譯后的HasPermission方法就會(huì)根據(jù)用戶名進(jìn)行權(quán)限檢查,程序中通過(guò)Debug.WriteLine輸出的調(diào)試信息也會(huì)輸出到DebugView中,也就相當(dāng)于Release模式下的效果。 其實(shí)DEBUG常量與Debug、Release模式并無(wú)太大的關(guān)系,唯一的關(guān)系就是,VS生成的項(xiàng)目中,Debug模式下,默認(rèn)會(huì)選中“定義DEBUG常量”,而Release模式下,默認(rèn)不會(huì)選中。也就是說(shuō),Debug模式,默認(rèn)會(huì)定義DEBUG常量,而Release不會(huì),如下圖: 既然DEBUG常量與Debug模式無(wú)本質(zhì)上的關(guān)聯(lián),那么為什么說(shuō)到Debug,就認(rèn)為DEBUG呢。道理其實(shí)很簡(jiǎn)單,世上本無(wú)路,走的人多了,便成了路。本來(lái)這個(gè)DEBUG常量只是Debug模式下的默認(rèn)預(yù)定義的常量,只是因?yàn)榇蠹伊?xí)慣了,并且對(duì)它的這種預(yù)定義還比較認(rèn)可,時(shí)間久了,就自然而然認(rèn)為DEBUG就代表Debug模式。 雖然我們可以通過(guò)去掉DEBUG常量,來(lái)使條件編譯在Debug模式下達(dá)到Release模式的效果,但是建議最好不要這樣做,因?yàn)檫@就像是大家普遍都認(rèn)可的一個(gè)約定,如果你一反常態(tài),不遵守這個(gè)約定,對(duì)于程序,編譯沒(méi)有問(wèn)題,但是后續(xù)維護(hù)肯定會(huì)相當(dāng)麻煩,所以還請(qǐng)大俠手下留情。 使用自定義的編譯常量DEBUG常量作為一種普遍的約定,最好不要打破。如果有除DEBUG外的條件編譯需要,可以使用自定義的編譯常量。 自定義編譯常量有兩種方法:
我們可以在條件編譯的輸入框中,定義自己的編譯常量,多個(gè)常量之間用分號(hào)“;”隔開(kāi),而且支持中文常量,如下: 當(dāng)然,我們也可以在代碼中通過(guò)#define預(yù)處理來(lái)定義,比如: #define 緣生夢(mèng)#define hbccdf 但是有一點(diǎn)需要注意,define定義常量必須在using 命名空間之前,否則會(huì)造成編譯錯(cuò)誤。 通過(guò)VS和Resharper改變顏色來(lái)查看哪些代碼真正會(huì)被編譯引入條件編譯后,我們可以通過(guò)VS很快知道哪些代碼會(huì)被編譯: 雖然條件編譯的代碼可以很直觀的看出來(lái),但是Conditional修飾的方法就看不出來(lái)了,這時(shí)就要借助神器Resharper了,如下圖: 從圖中看出,release模式,由于沒(méi)有定義DEBUG、緣生夢(mèng)兩個(gè)常量,所以,調(diào)用Test、Debug.WriteLine方法的地方就會(huì)變暗,這樣很直觀就知道這些代碼不會(huì)被編譯。再次說(shuō)明一個(gè)道理,神器對(duì)于開(kāi)發(fā)有很大的幫助作用。 通過(guò)反編譯查看生成后的代碼上面總是說(shuō),有些代碼會(huì)被編譯,有的則不會(huì),那么真正編譯后的效果是怎樣的,我們不妨使用另外一個(gè)比較強(qiáng)大的工具,反編譯工具Reflector,來(lái)查看一下反編譯后的代碼: 從圖中的反編譯后的代碼可以看出,滿足條件的代碼會(huì)真正編譯到生成的程序集里,而不滿足的代碼則不會(huì)生成到程序集里。 Conditional修飾的方法,會(huì)有方法的實(shí)現(xiàn),但是沒(méi)有方法的調(diào)用。不適合在方法里做一些變量的修改。 總結(jié)Conditional和條件編譯
TRACE常量從上面多幅圖中,可以看到,在Debug和Release模式下都會(huì)定義一個(gè)TRACE常量。 現(xiàn)在我們知道DEBUG常量是用來(lái)控制調(diào)用Debug.WriteLine的語(yǔ)句是否被編譯。那么TRACE常量呢。 相信很多人也用過(guò)System.Diagnostics.Trace類,由DEBUG常量與Debug有關(guān),可以想到TRACE常量與Trace有關(guān),我們來(lái)看一下MSDN對(duì)于Trace的定義。 從MSDN中,看到Trace類的定義以及可以調(diào)用的方法與Debug類似,都有WriteLine方法。下面是Trace的WriteLine方法定義: [ConditionalAttribute("TRACE")]public static void WriteLine(string message 由此可以知道,TRACE常量是用來(lái)控制Trace類中WriteLine等方法是否被編譯的作用。 Debug類與Trace類的區(qū)別到現(xiàn)在為止,我們漸漸的了解了Debug類與Trace類,他們都可以通過(guò)WriteLine方法輸出調(diào)試或跟蹤信息,從他們的定義和暴露的方法中,可以看出他們非常相似,那么他們有什么區(qū)別呢。 有些人會(huì)根據(jù)使用的效果總結(jié)為:Debug類只能在Debug模式下執(zhí)行,在Release模式下無(wú)效果,而Trace類在Debug和Release模式下都可以執(zhí)行。 確實(shí),在VS中新建一個(gè)項(xiàng)目,分別調(diào)用Debug.WriteLine和Trace.WriteLine方法,只有Trace.WriteLine輸出的信息,在Release模式下有效。這也看似驗(yàn)證了上面的區(qū)別。 但這是他們兩個(gè)真正的區(qū)別嗎。我們一點(diǎn)一點(diǎn)來(lái)分析。 首先看這兩個(gè)方法的定義: 由圖看到,每個(gè)方法都通過(guò)ConditionalAttribute關(guān)聯(lián)了一個(gè)條件編譯常量,Debug關(guān)聯(lián)DEBUG常量,Trace關(guān)聯(lián)TRACE常量。再來(lái)看一下這兩個(gè)常量的定義: 從圖中看到,TRACE在Debug和Release模式下都會(huì)定義,這樣在Debug和Release模式下都會(huì)執(zhí)行,而DEBUG只在Debug模式下才會(huì)定義,Debug.WriteLine只在Debug模式下執(zhí)行。從而也驗(yàn)證了上面的結(jié)論。 但DEBUG與TRACE只是在默認(rèn)的情況下的定義。當(dāng)改變這些定義后,上面的結(jié)論就不再正確。 我們來(lái)做這樣的實(shí)驗(yàn):Debug模式下只定義TRACE常量,Release模式只定義DEBUG常量 然后在Debug和Release模式下分別執(zhí)行Debug.WriteLine方法和Trace.WriteLine方法,Debug.WriteLine方法只在Release模式下有效,而Trace.WriteLine方法只在Debug模式下有效。上面的結(jié)論不再成立。 為了更好的驗(yàn)證一下我們的結(jié)論,對(duì)他們進(jìn)行反編譯: 由圖中的反編譯代碼看到,除了關(guān)聯(lián)的條件編譯常量不同外,內(nèi)部調(diào)用的方法均為TraceInternal.WriteLine,實(shí)現(xiàn)完全一樣。 那么下面來(lái)總結(jié)一下Debug與Trace的區(qū)別:
Debug、Debugger、Kernel32的聯(lián)系每個(gè)人大腦的空間都是有限的,零散的知識(shí)很容易忘掉,輸出調(diào)試信息到DebugView中的三種方法,由于關(guān)聯(lián)性不是很強(qiáng),很容易會(huì)忘掉其中的一兩種,那么既然實(shí)現(xiàn)相同的功能,他們之間有什么關(guān)聯(lián)嗎。 這就要從Debug的MSDN文檔說(shuō)起。 我們知道Debug編譯的程序,運(yùn)行的時(shí)候可以通過(guò)Debug.WriteLine方法輸出調(diào)試i信息到DebugView,而MSDN中的解釋 將后跟行結(jié)束符的消息寫(xiě)入 Listeners 集合中的跟蹤偵聽(tīng)器。 并沒(méi)有說(shuō)是輸出到DebugView中,而是寫(xiě)入到Listeners集合中的跟蹤偵聽(tīng)器。為了弄明白原理,有必要深入的研究一下。這時(shí)就要依賴反編譯工具Reflector了。 從整體看上去,Debug類的每一個(gè)方法都通過(guò)Conditional與DEBUG常量管理,也就是默認(rèn)情況下,Debug類的所有方法在Debug模式下均不會(huì)編譯。 我們?cè)賮?lái)具體看一下Debug.WriteLine方法: [Conditional("DEBUG"), __DynamicallyInvokable]public static void WriteLine(string message) 再來(lái)看一下TraceInternal方法: public static void WriteLine(string message) { foreach (TraceListener listener in Listeners) { listener.WriteLine(message); if (AutoFlush) { listener.Flush(); } } return; } 上面的代碼是精簡(jiǎn)后的代碼。從代碼中看到會(huì)調(diào)用集合中的每一個(gè)listener的WriteLine方法。 那么這些listener的WriteLine又做了什么呢: public abstract class TraceListener : MarshalByRefObject, IDisposable 原來(lái)TraceListener的WriteLine是抽象類的抽象方法,那么我們得到的listener是具體類的一個(gè)抽象,相當(dāng)于接口,這是微軟的一貫做法,再繼續(xù)下去,就需要知道是哪些具體的TraceListener了。 繼續(xù)上面的線索,我們是從Listeners屬性中獲取到的TraceListener,那么就去看Listeners的get實(shí)現(xiàn): public static TraceListenerCollection Listeners { get { InitializeSettings(); if (listeners == null) { lock (critSec) { if (listeners == null) { SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection; if (systemDiagnosticsSection != null) { //從配置文件獲取listener,但是由于此處沒(méi)有設(shè)置配置文件,所以先不研究這個(gè)地方 listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject(); } else { //這里new了一個(gè)TraceListener的集合 listeners = new TraceListenerCollection(); //這里我們看到了TraceListener的具體實(shí)現(xiàn)類DefaultTraceListener TraceListener listener = new DefaultTraceListener { IndentLevel = indentLevel, IndentSize = indentSize }; listeners.Add(listener); } } } } return listeners; } } 從代碼中,找到了具體的實(shí)現(xiàn)類DefaultTraceListener,那么就快點(diǎn)看看他的WriteLine方法吧,有點(diǎn)迫不及待了。 實(shí)際上,WriteLine方法會(huì)調(diào)用內(nèi)部的Write方法: private void Write(string message, bool useLogFile) { if (base.NeedIndent) { //寫(xiě)縮進(jìn),實(shí)際是空格 this.WriteIndent(); } if ((message == null) || (message.Length <= 16384)) { //輸出消息 this.internalWrite(message); } else { //當(dāng)消息很長(zhǎng)時(shí),會(huì)通過(guò)internalWrite方法將消息多次輸出 int startIndex = 0; while (startIndex < (message.Length - 16384)) { this.internalWrite(message.Substring(startIndex, 16384)); startIndex += 16384; } this.internalWrite(message.Substring(startIndex)); } if (useLogFile && (this.LogFileName.Length != 0)) { //輸出到日志文件中 this.WriteToLogFile(message, false); } } 與輸出信息有關(guān)的有兩個(gè)地方,一個(gè)是調(diào)用internalWrite,另外一個(gè)是WriteToLogFile。從WriteToLogFile是有執(zhí)行條件的。感興趣的可以研究一下。重點(diǎn)來(lái)看一下internalWrite方法。 private void internalWrite(string message) { if (Debugger.IsLogging()) { //調(diào)用Debugger.Log方法,這個(gè)方法可以輸出信息到DebugView中 Debugger.Log(0, null, message); } else if (message == null) { SafeNativeMethods.OutputDebugString(string.Empty); } else { //調(diào)用Native方法 SafeNativeMethods.OutputDebugString(message); } } internalWrite中調(diào)用了兩個(gè)比較重要的方法。其中Debugger.Log方法是不是很熟悉呢。我們剛剛在上面總結(jié)了三種輸出調(diào)試信息到DebugView的方法,其中就包含了Debugger.Log,而Debug.WriteLine方法中,又會(huì)調(diào)用到Debugger.Log方法,這樣,這兩個(gè)方法就建立起了聯(lián)系。 再來(lái)看SafeNativeMethods.OutputDebugString,看到這個(gè)類的命名,以及對(duì)Win32API的了解,就能想到,這肯定是通過(guò)PInvoke等方法對(duì)Win32API的封裝,看其定義: [DllImport("kernel32.dll", CharSet=CharSet.Auto)]public static extern void OutputDebugString(string message); 果然,這就是對(duì)kernel32.dll中的OutputDebugString的封裝調(diào)用,而這又是上面三種方法其中的一種。好了,這樣Debug、Debugger、OutputDebugString就全都聯(lián)系到了一起,他們之間有了聯(lián)系,是不是就更容易記憶了。 配置TraceListener從上面對(duì)Debug.WriteLine的一步步跟蹤分析的過(guò)程中,我們看到了對(duì)于TraceListener的獲取,一種方式是DefaultTraceListener,另外一種就是如下: SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection; if (systemDiagnosticsSection != null) { //從配置文件獲取listener,但是由于此處沒(méi)有設(shè)置配置文件,所以先不研究這個(gè)地方 listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject(); } 這幾行代碼就是從配置文件的system.diagnostics中獲取TraceListener。 關(guān)于配置文件具體的讀取和解析過(guò)程,本文就不再詳細(xì)介紹了,感興趣的朋友可以自行研究,或者等到我的后面博文詳細(xì)介紹。 那么現(xiàn)在主要說(shuō)一下如何通過(guò)配置文件來(lái)設(shè)置TraceListener,如下: <configuration> <system.diagnostics> <trace autoflush="true" indentsize="2"> <listeners> <add name="myListener" type="System.Diagnostics.ConsoleTraceListener" /> </listeners> </trace> </system.diagnostics> </configuration> 同樣,有配置文件,那么就可以通過(guò)代碼來(lái)實(shí)現(xiàn)同樣的功能,如下: Debug.Listeners.Add(new ConsoleTraceListener());Debug.AutoFlush = true;Debug.WriteLine("這是Debug.WriteLine輸出的調(diào)試信息"); Debug編譯與Release編譯的區(qū)別Debug與Release的區(qū)別,這個(gè)問(wèn)題,相信很多人都會(huì)有疑問(wèn),也會(huì)有很多人有自己的答案,我聽(tīng)到過(guò)的答案有這些:
這些答案看上去好像都對(duì),但是為什么Debug與Release有這么大的區(qū)別呢,這就需要深入的思考一下。 在Debug類與Trace類的區(qū)別一節(jié)中,相信有些朋友已經(jīng)明白,Debug編譯與Release編譯的區(qū)別其實(shí)是編譯配置的不同,DEBUG、TRACE常量的定義就是其中不同的地方。那么Debug編譯與Release編譯還有什么不同呢。針對(duì)這個(gè)問(wèn)題,我總結(jié)了一下,主要區(qū)別有下面幾點(diǎn):
接下來(lái),我們?cè)敿?xì)的看一下。 常量定義不同這個(gè)相信大家已經(jīng)非常清楚了,Debug模式下會(huì)定義DEBUG、TRACE常量,而Release模式下只定義TRACE常量,如下圖所示 優(yōu)化代碼不同Debug模式下,默認(rèn)不會(huì)進(jìn)行代碼的優(yōu)化,而Release模式下,由于默認(rèn)選中了“優(yōu)化代碼”選項(xiàng),所以編譯器會(huì)對(duì)生成的代碼進(jìn)行優(yōu)化,以提高運(yùn)行速度。 包含的調(diào)試信息不同編譯生成的屬性頁(yè)中有一個(gè)“高級(jí)”按鈕,點(diǎn)擊后會(huì)彈出一個(gè)對(duì)話框,然后可以對(duì)編譯生成進(jìn)行一些設(shè)置,如下 Debug與Release的高級(jí)選項(xiàng)對(duì)比: 從圖中看到,Debug模式下默認(rèn)會(huì)生成全部的調(diào)試信息,而Release模式下只生成pdb文件。 輸出路徑不同這一點(diǎn)詳細(xì)大家都很清楚了,Debug模式下會(huì)輸出到Debug目錄,Release模式下會(huì)輸出到Release目錄,如圖: Debug編譯與Release編譯的區(qū)別總結(jié)說(shuō)了這么多,我們?cè)賮?lái)思考本節(jié)開(kāi)始時(shí)說(shuō)到的關(guān)于Debug編譯與Release編譯的區(qū)別的答案,可以得出這樣的結(jié)論:
知道這些區(qū)別后,我們就可以通過(guò)修改編譯配置來(lái)使Debug程序達(dá)到Release的效果,使Release程序達(dá)到Debug的效果。當(dāng)然,還是那句話,雖然可以實(shí)現(xiàn)這樣的效果,但是建議絕對(duì)不要這樣做,不然程序維護(hù)起來(lái),肯定會(huì)遇到很多問(wèn)題。 總結(jié)的重要性有人說(shuō),互聯(lián)網(wǎng)這么發(fā)達(dá),遇到什么問(wèn)題直接谷歌百度,內(nèi)事不決問(wèn)百度,外事不決問(wèn)谷歌。但是即使你通過(guò)網(wǎng)絡(luò)解決了問(wèn)題,如果不消化吸收,經(jīng)過(guò)總結(jié)變成自己的東西,那么下次還會(huì)遇到同樣的問(wèn)題。書(shū)到用時(shí)方恨少,一回首,已白了少年頭。如果不總結(jié),任他虐我千百遍,我卻視他如初見(jiàn),從網(wǎng)絡(luò)上找到的方法、代碼、解決方案,那是別人的知識(shí),只有經(jīng)過(guò)實(shí)踐去驗(yàn)證,通過(guò)總結(jié),消化吸收,才能將這些知識(shí)“據(jù)為己有”,為我所用。 那么至少?gòu)默F(xiàn)在做起,把每天學(xué)到的知識(shí),遇到的問(wèn)題,得到的經(jīng)驗(yàn),總結(jié)下來(lái),相信自己,這是大牛的節(jié)奏。 該文章在 2021/3/8 15:03:17 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |