一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

C#調(diào)試入門篇

 圓錐的布袋 2018-09-15

DotNet程序的調(diào)試,是DotNet程序員必備的技能之一,開發(fā)出穩(wěn)定的程序、解決程序的疑難雜癥都需要很強大的調(diào)試能力。DotNet調(diào)試有很多方法和技巧?,F(xiàn)在本文就介紹一下借助DebugView工具進(jìn)行調(diào)試的方法,以及由DebugView引申出來的知識點。

DebugView

DebugView是一個查看調(diào)試信息的非常棒的工具,支持Debug、Release模式編譯的程序,甚至支持內(nèi)核程序,而且能夠定制各種過濾條件,讓你只看到關(guān)心的輸出信息,而且可以定制高亮顯示的內(nèi)容等等,非常方便。

debugview

捕捉Release模式的Win32程序輸出的調(diào)試信息,需要選中Capture Global Win32選項:

debugview_release

過濾與高亮功能

debugview_過濾與高亮

可以通過include、exclude設(shè)置過濾條件,包含指定字符串的輸出信息將會被過濾。還可以通過exclude條件過濾掉對應(yīng)進(jìn)程ID的調(diào)試信息。多個條件使用“;”分隔,而且支持“*”通配符。

遠(yuǎn)程調(diào)試

DebugView支持遠(yuǎn)程捕捉調(diào)試信息。首先在遠(yuǎn)程機器上通過如下命令啟動DebugView:

DebugView.exe /a /t /g /s

這樣,DebugView就會以服務(wù)的方式運行,如下圖:

debugview服務(wù)

然后在本地機器上啟動DebugView,并通過Connect連接到遠(yuǎn)程機器的DebugView,當(dāng)遠(yuǎn)程機器中有調(diào)試信息輸出時,本地就會捕獲到,并展示出來:

debugview_connect

輸出信息到DebugView的幾種方式

DebugView的一些功能是不是讓你心動了呢。俗話說心動不如行動,但是在行動之前,首先要知道C#如何將調(diào)試信息輸出到DebugView中。

通過編程輸出一些調(diào)試信息到DebugView中,一共有三種方式:

  • Debug.WriteLine
  • Debugger.Log
  • Kernal32.dll中的OutputDebugString方法

一、Debug.WriteLine

通過Debug.WriteLine可以將調(diào)試信息寫入到DebugView中,如下:

Debug.WriteLine("這是調(diào)試信息");

效果如下:

Debug_DebugView

不過此方式只能在Debug模式下有效。具體原因下面會詳細(xì)介紹。

二、Debugger.Log

Debug.WriteLine已經(jīng)很好用了,唯一的缺點就是在Release模式下無效。那么在Release模式下就可以使用Debugger.Log方法,示例如下:

Debugger.Log(0, null, "這是Debugger.Log輸出的調(diào)試信息");

三、Kernel32.dll中的OutputDebugString方法

做C++開發(fā)的應(yīng)該知道可以通過OutputDebugString這個API開實現(xiàn)輸出調(diào)試信息到DebugView中吧。那么C++能做的,C#也能做??梢酝ㄟ^PInvoke的方式引入此方法,這個API屬于Kernel32.dll,如下聲明:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern void OutputDebugString(string message);

然后就可以通過調(diào)用此方法,將調(diào)試信息輸出到DebugView中。

DebugView與日志框架比較

可能有人會說,DebugView能做的事情,我用log4Net,NLog等日志框架也能做,為什么要用DebugView呢?

問的好,那么我就根據(jù)平時使用DebugView的經(jīng)驗來給你一個用DebugView的理由:

  • DebugView使用非常方便。相比于日志框架龐大的體系,DebugView的使用可謂是十分的簡單方便。DebugView只有幾百K的大小,占用空間幾乎可以忽略不計。從官網(wǎng)下載后,直接運行exe,幾乎不需要任何配置就可以正常使用。而相比于DebugView,日志框架可以算的上龐然大物。而當(dāng)你從官網(wǎng)獲得log4Net后,需要進(jìn)行各種繁雜的配置。甚至你要花上幾天時間專門學(xué)習(xí)一下這套框架。由此可以看出DebugView的使用實在是方便的不能再方便。
  • DebugView是可視化工具,支持各種過濾和高亮。DebugView可以通過過濾條件來過濾不關(guān)心的信息,只顯示相關(guān)的調(diào)試信息。而日志框架輸出的是文件等文本信息,這些信息會包含程序運行過程中的所有信息,雖然可以通過配置文件來指定只輸出哪一類信息,但是不如DebugView來的方便簡單。
  • DebugView可以實時監(jiān)視。DebugView中有“自動滾動”的功能,程序中輸出的調(diào)試信息,基本上瞬間就會在DebugView中展示出來,當(dāng)由于大量信息導(dǎo)致DebugView中的文本框滿了后,DebugView可以通過自動滾動滾動條,讓你隨時都可以看到最新的一條信息,達(dá)到類似監(jiān)視的效果。而日志框架由于其寫文本的特性,很難達(dá)到這種效果,即使能達(dá)到,相信也是需要對日志框架相當(dāng)清楚了解,才能完成這個效果。

這些理由應(yīng)該足以讓你使用DebugView了吧。使用DebugView的理由肯定還不止這些,如果你有更好的理由,還請分享出來。

當(dāng)然,DebugView與日志框架,每個都有每個的用途。通過DebugView的方式,只適合短暫的調(diào)試,而正式發(fā)布的網(wǎng)站或者軟件,需要一套記錄程序長期以來的運行狀態(tài)的工具,那么就非日志框架莫屬了。所以DebugView與日志框架,要在合適的地方,發(fā)揮他們最大的功效。

聲明

Log4Net等日志框架,功能足夠強大,也足夠豐富,相信上面說到的DebugView的功能,也可以通過日志框架來實現(xiàn)。但是和DebugView比較起來,會相對復(fù)雜一些。所以上面說到的使用DebugView的理由是基于方便性的比較,DebugView有足夠的方便性來讓你選擇使用他。

ConditionalAttribute詳解與條件編譯

說到調(diào)試,那么肯定有開發(fā)人員遇到這種情況,開發(fā)產(chǎn)品的時候,遇到一些問題,就在代碼中加入了大量的調(diào)試輸出信息,比如通過Console.WriteLine、MessageBox.Show或者通過Ilog.Log記錄日志,甚至臨時改變邏輯來驗證邏輯的正確性等。經(jīng)過這些調(diào)試信息的幫助,終于解決了產(chǎn)品的問題。但此時又遇到了新的問題,產(chǎn)品最終發(fā)布的時候,肯定是不能有這些調(diào)試信息的,可是已經(jīng)加了這么多調(diào)試信息,難道要全部刪除掉嗎。這顯然不是一個好辦法,這么多代碼,手一抖,很容易就刪除了不相關(guān)的代碼,造成不可預(yù)估的后果。

做過C/C++開發(fā)的,可以從各種跨平臺的開源庫中看到,一堆一堆的#if....#else....#endif,這就是條件編譯,這也是C/C++跨平臺所依賴的最基本的東西,在Linux系統(tǒng),編譯這段代碼,在Windows系統(tǒng)又編譯那段代碼,最終實現(xiàn)了代碼級別的跨平臺。

那么C#中有沒有類似的功能呢,答案當(dāng)然是有,而且有兩種:

  • 通過給方法加上ConditionalAttribute特性
  • 使用#if..#else..#endif,來控制代碼的編譯

ConditionalAttribute特性

下面是ConditionalAttribute的構(gòu)造函數(shù):

public ConditionalAttribute(
    string conditionString
)

構(gòu)造函數(shù)中的參數(shù)conditionString,是一個區(qū)分大小寫的條件編譯符號的字符串。

上面提到Debug.WriteLine時,說到這個功能只在Debug模式下才有用,Release下就不起作用了。

我們從MSDN中看一下Debug.WriteLine的說明:

[ConditionalAttribute("DEBUG")]
public static void WriteLine(
    string message
)

由此也就明白了Debug.WriteLine只能在Debug模式下使用,而在Release模式下無效的原因了。

條件編譯#if..#else..#endif

C/C++中有#if..#else..#endif,C#中也有這些,他們都被稱為預(yù)處理器。通過預(yù)定義的條件編譯符號,來控制編譯時編譯哪部分代碼。如下:

復(fù)制代碼
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
}
復(fù)制代碼

預(yù)定義的Debug宏在什么地方

說到條件編譯,是不是只有DEBUG 和 RELEASE兩種情況呢,如果是這種情況的話,那也就是說DEBUG和RELEASE兩種情況是定義好了的,俗話說就是“做死了”,這是作死的節(jié)奏啊。不作死就不會死,至少VS在這點上還沒有作死。

讓我們來一步步揭開DEBUG的面紗。

既然是條件編譯,那么就應(yīng)該和編譯選項有關(guān)。我們知道C#項目,有一個屬性頁,可以設(shè)置很多編譯的選項,如下:

VS項目生成屬性頁

從圖中看到,條件編譯是用的DEBUG常量,或者稱為DEBUG條件編譯符號,是在這個編譯生成選項中定義的,如果去掉這個定義,那么編譯后的HasPermission方法就會根據(jù)用戶名進(jìn)行權(quán)限檢查,程序中通過Debug.WriteLine輸出的調(diào)試信息也會輸出到DebugView中,也就相當(dāng)于Release模式下的效果。

其實DEBUG常量與Debug、Release模式并無太大的關(guān)系,唯一的關(guān)系就是,VS生成的項目中,Debug模式下,默認(rèn)會選中“定義DEBUG常量”,而Release模式下,默認(rèn)不會選中。也就是說,Debug模式,默認(rèn)會定義DEBUG常量,而Release不會,如下圖:

Debug_Release默認(rèn)配置

既然DEBUG常量與Debug模式無本質(zhì)上的關(guān)聯(lián),那么為什么說到Debug,就認(rèn)為DEBUG呢。道理其實很簡單,世上本無路,走的人多了,便成了路。本來這個DEBUG常量只是Debug模式下的默認(rèn)預(yù)定義的常量,只是因為大家習(xí)慣了,并且對它的這種預(yù)定義還比較認(rèn)可,時間久了,就自然而然認(rèn)為DEBUG就代表Debug模式。

雖然我們可以通過去掉DEBUG常量,來使條件編譯在Debug模式下達(dá)到Release模式的效果,但是建議最好不要這樣做,因為這就像是大家普遍都認(rèn)可的一個約定,如果你一反常態(tài),不遵守這個約定,對于程序,編譯沒有問題,但是后續(xù)維護(hù)肯定會相當(dāng)麻煩,所以還請大俠手下留情。

使用自定義的編譯常量

DEBUG常量作為一種普遍的約定,最好不要打破。如果有除DEBUG外的條件編譯需要,可以使用自定義的編譯常量。

自定義編譯常量有兩種方法:

  • 通過編譯生成屬性頁中的條件編譯輸入框,定義自己的編譯常量。
  • 在代碼中使用#define預(yù)處理,來定義編譯常量。

我們可以在條件編譯的輸入框中,定義自己的編譯常量,多個常量之間用分號“;”隔開,而且支持中文常量,如下:

自定義條件編譯符號

當(dāng)然,我們也可以在代碼中通過#define預(yù)處理來定義,比如:

#define 緣生夢
#define hbccdf

但是有一點需要注意,define定義常量必須在using 命名空間之前,否則會造成編譯錯誤。

通過VS和Resharper改變顏色來查看哪些代碼真正會被編譯

引入條件編譯后,我們可以通過VS很快知道哪些代碼會被編譯:

VS_debug_release顏色對比

雖然條件編譯的代碼可以很直觀的看出來,但是Conditional修飾的方法就看不出來了,這時就要借助神器Resharper了,如下圖:

resharper_debug_release顏色對比

從圖中看出,release模式,由于沒有定義DEBUG、緣生夢兩個常量,所以,調(diào)用Test、Debug.WriteLine方法的地方就會變暗,這樣很直觀就知道這些代碼不會被編譯。再次說明一個道理,神器對于開發(fā)有很大的幫助作用。

通過反編譯查看生成后的代碼

上面總是說,有些代碼會被編譯,有的則不會,那么真正編譯后的效果是怎樣的,我們不妨使用另外一個比較強大的工具,反編譯工具Reflector,來查看一下反編譯后的代碼:

debug_release_反編譯代碼對比

從圖中的反編譯后的代碼可以看出,滿足條件的代碼會真正編譯到生成的程序集里,而不滿足的代碼則不會生成到程序集里。

Conditional修飾的方法,會有方法的實現(xiàn),但是沒有方法的調(diào)用。不適合在方法里做一些變量的修改。

總結(jié)Conditional和條件編譯

  • Conditional為方法級別的條件編譯,只能修飾方法;#if..#else..#endif為行級別的條件編譯,可以指定任意代碼。
  • Conditional只能修飾無返回值的方法,而且不適合在方法中處理一些變量的值。#if..#else..#endif沒有任何限制。
  • 可以通過條件編譯符號輸入框,或者代碼中的#define定義條件編譯常量,這些常量對于Conditional、#if..#else..#endif均有效。

TRACE常量

從上面多幅圖中,可以看到,在Debug和Release模式下都會定義一個TRACE常量。

現(xiàn)在我們知道DEBUG常量是用來控制調(diào)用Debug.WriteLine的語句是否被編譯。那么TRACE常量呢。

相信很多人也用過System.Diagnostics.Trace類,由DEBUG常量與Debug有關(guān),可以想到TRACE常量與Trace有關(guān),我們來看一下MSDN對于Trace的定義。

從MSDN中,看到Trace類的定義以及可以調(diào)用的方法與Debug類似,都有WriteLine方法。下面是Trace的WriteLine方法定義:

[ConditionalAttribute("TRACE")]
public static void WriteLine(
    string message
)

由此可以知道,TRACE常量是用來控制Trace類中WriteLine等方法是否被編譯的作用。

Debug類與Trace類的區(qū)別

到現(xiàn)在為止,我們漸漸的了解了Debug類與Trace類,他們都可以通過WriteLine方法輸出調(diào)試或跟蹤信息,從他們的定義和暴露的方法中,可以看出他們非常相似,那么他們有什么區(qū)別呢。

有些人會根據(jù)使用的效果總結(jié)為:Debug類只能在Debug模式下執(zhí)行,在Release模式下無效果,而Trace類在Debug和Release模式下都可以執(zhí)行。

確實,在VS中新建一個項目,分別調(diào)用Debug.WriteLine和Trace.WriteLine方法,只有Trace.WriteLine輸出的信息,在Release模式下有效。這也看似驗證了上面的區(qū)別。

但這是他們兩個真正的區(qū)別嗎。我們一點一點來分析。

首先看這兩個方法的定義:

MSDN_Debug_Trace對比

由圖看到,每個方法都通過ConditionalAttribute關(guān)聯(lián)了一個條件編譯常量,Debug關(guān)聯(lián)DEBUG常量,Trace關(guān)聯(lián)TRACE常量。再來看一下這兩個常量的定義:

常量定義_Debug_Release對比

從圖中看到,TRACE在Debug和Release模式下都會定義,這樣在Debug和Release模式下都會執(zhí)行,而DEBUG只在Debug模式下才會定義,Debug.WriteLine只在Debug模式下執(zhí)行。從而也驗證了上面的結(jié)論。

但DEBUG與TRACE只是在默認(rèn)的情況下的定義。當(dāng)改變這些定義后,上面的結(jié)論就不再正確。

我們來做這樣的實驗:Debug模式下只定義TRACE常量,Release模式只定義DEBUG常量

常量測試_Debug_Release對比

然后在Debug和Release模式下分別執(zhí)行Debug.WriteLine方法和Trace.WriteLine方法,Debug.WriteLine方法只在Release模式下有效,而Trace.WriteLine方法只在Debug模式下有效。上面的結(jié)論不再成立。

為了更好的驗證一下我們的結(jié)論,對他們進(jìn)行反編譯:

反編譯_Debug_Trace.WriteLine對比

由圖中的反編譯代碼看到,除了關(guān)聯(lián)的條件編譯常量不同外,內(nèi)部調(diào)用的方法均為TraceInternal.WriteLine,實現(xiàn)完全一樣。

那么下面來總結(jié)一下Debug與Trace的區(qū)別:

  • Debug類關(guān)聯(lián)DEBUG常量,Trace類關(guān)聯(lián)TRACE常量。
  • 默認(rèn)情況下,VS創(chuàng)建的項目Debug模式會定義DEBUG與TRACE,而Release模式只定義TRACE,使Debug只在Debug模式有效,而Trace在Debug和Release模式均有效。
  • 可以通過修改DEBUG、TRACE的默認(rèn)定義,來改變Debug、Trace的默認(rèn)行為。但是建議最好不要這樣做,因為改變了這種默認(rèn)的約定可能會出現(xiàn)意想不到的問題。

Debug、Debugger、Kernel32的聯(lián)系

每個人大腦的空間都是有限的,零散的知識很容易忘掉,輸出調(diào)試信息到DebugView中的三種方法,由于關(guān)聯(lián)性不是很強,很容易會忘掉其中的一兩種,那么既然實現(xiàn)相同的功能,他們之間有什么關(guān)聯(lián)嗎。

這就要從Debug的MSDN文檔說起。

我們知道Debug編譯的程序,運行的時候可以通過Debug.WriteLine方法輸出調(diào)試i信息到DebugView,而MSDN中的解釋

將后跟行結(jié)束符的消息寫入 Listeners 集合中的跟蹤偵聽器。

并沒有說是輸出到DebugView中,而是寫入到Listeners集合中的跟蹤偵聽器。為了弄明白原理,有必要深入的研究一下。這時就要依賴反編譯工具Reflector了。

Debug類整體觀賞

從整體看上去,Debug類的每一個方法都通過Conditional與DEBUG常量管理,也就是默認(rèn)情況下,Debug類的所有方法在Debug模式下均不會編譯。

我們再來具體看一下Debug.WriteLine方法:

[Conditional("DEBUG"), __DynamicallyInvokable]
public static void WriteLine(string message)
{
    TraceInternal.WriteLine(message);
}

再來看一下TraceInternal方法:

復(fù)制代碼
public static void WriteLine(string message)
{
    foreach (TraceListener listener in Listeners)
    {
        listener.WriteLine(message);
        if (AutoFlush)
        {
            listener.Flush();
        }
    }
    return;
}
復(fù)制代碼

上面的代碼是精簡后的代碼。從代碼中看到會調(diào)用集合中的每一個listener的WriteLine方法。

那么這些listener的WriteLine又做了什么呢:

public abstract class TraceListener : MarshalByRefObject, IDisposable
{
    public abstract void WriteLine(string message);
}

原來TraceListener的WriteLine是抽象類的抽象方法,那么我們得到的listener是具體類的一個抽象,相當(dāng)于接口,這是微軟的一貫做法,再繼續(xù)下去,就需要知道是哪些具體的TraceListener了。

繼續(xù)上面的線索,我們是從Listeners屬性中獲取到的TraceListener,那么就去看Listeners的get實現(xiàn):

復(fù)制代碼
public static TraceListenerCollection Listeners
{
    get
    {
        InitializeSettings();
        if (listeners == null)
        {
            lock (critSec)
            {
                if (listeners == null)
                {
                    SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
                    if (systemDiagnosticsSection != null)
                    {
                        //從配置文件獲取listener,但是由于此處沒有設(shè)置配置文件,所以先不研究這個地方
                            listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
                    }
                    else
                    {
                        //這里new了一個TraceListener的集合
                            listeners = new TraceListenerCollection();
                        //這里我們看到了TraceListener的具體實現(xiàn)類DefaultTraceListener
                        TraceListener listener = new DefaultTraceListener {
                            IndentLevel = indentLevel,
                            IndentSize = indentSize
                        };
                        listeners.Add(listener);
                    }
                }
            }
        }
        return listeners;
    }
}
復(fù)制代碼

從代碼中,找到了具體的實現(xiàn)類DefaultTraceListener,那么就快點看看他的WriteLine方法吧,有點迫不及待了。

實際上,WriteLine方法會調(diào)用內(nèi)部的Write方法:

復(fù)制代碼
private void Write(string message, bool useLogFile)
{
    if (base.NeedIndent)
    {
         //寫縮進(jìn),實際是空格
          this.WriteIndent();
    }
    if ((message == null) || (message.Length <= 16384))
    {
         //輸出消息
          this.internalWrite(message);
    }
    else
    {
        //當(dāng)消息很長時,會通過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);
    }
}
復(fù)制代碼

與輸出信息有關(guān)的有兩個地方,一個是調(diào)用internalWrite,另外一個是WriteToLogFile。從WriteToLogFile是有執(zhí)行條件的。感興趣的可以研究一下。重點來看一下internalWrite方法。

復(fù)制代碼
private void internalWrite(string message)
{
    if (Debugger.IsLogging())
    {
       //調(diào)用Debugger.Log方法,這個方法可以輸出信息到DebugView中
        Debugger.Log(0, null, message);
    }
    else if (message == null)
    {
        SafeNativeMethods.OutputDebugString(string.Empty);
    }
    else
    {
       //調(diào)用Native方法
        SafeNativeMethods.OutputDebugString(message);
    }
}
復(fù)制代碼

internalWrite中調(diào)用了兩個比較重要的方法。其中Debugger.Log方法是不是很熟悉呢。我們剛剛在上面總結(jié)了三種輸出調(diào)試信息到DebugView的方法,其中就包含了Debugger.Log,而Debug.WriteLine方法中,又會調(diào)用到Debugger.Log方法,這樣,這兩個方法就建立起了聯(lián)系。

再來看SafeNativeMethods.OutputDebugString,看到這個類的命名,以及對Win32API的了解,就能想到,這肯定是通過PInvoke等方法對Win32API的封裝,看其定義:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern void OutputDebugString(string message);

果然,這就是對kernel32.dll中的OutputDebugString的封裝調(diào)用,而這又是上面三種方法其中的一種。好了,這樣Debug、Debugger、OutputDebugString就全都聯(lián)系到了一起,他們之間有了聯(lián)系,是不是就更容易記憶了。

配置TraceListener

從上面對Debug.WriteLine的一步步跟蹤分析的過程中,我們看到了對于TraceListener的獲取,一種方式是DefaultTraceListener,另外一種就是如下:

復(fù)制代碼
SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
if (systemDiagnosticsSection != null)
{
     //從配置文件獲取listener,但是由于此處沒有設(shè)置配置文件,所以先不研究這個地方
    listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
}
復(fù)制代碼

這幾行代碼就是從配置文件的system.diagnostics中獲取TraceListener。

關(guān)于配置文件具體的讀取和解析過程,本文就不再詳細(xì)介紹了,感興趣的朋友可以自行研究,或者等到我的后面博文詳細(xì)介紹。

那么現(xiàn)在主要說一下如何通過配置文件來設(shè)置TraceListener,如下:

復(fù)制代碼
<configuration>
   <system.diagnostics>
      <trace autoflush="true" indentsize="2">
         <listeners>
            <add name="myListener" type="System.Diagnostics.ConsoleTraceListener" />
         </listeners>
      </trace>
   </system.diagnostics>
</configuration>
復(fù)制代碼

同樣,有配置文件,那么就可以通過代碼來實現(xiàn)同樣的功能,如下:

Debug.Listeners.Add(new ConsoleTraceListener());
Debug.AutoFlush = true;
Debug.WriteLine("這是Debug.WriteLine輸出的調(diào)試信息");

Debug編譯與Release編譯的區(qū)別

Debug與Release的區(qū)別,這個問題,相信很多人都會有疑問,也會有很多人有自己的答案,我聽到過的答案有這些:

  • Release比Debug運行速度快,執(zhí)行效率高。
  • Debug 是調(diào)試用的,可以打斷點執(zhí)行。Release是發(fā)布用的,不能打斷點執(zhí)行。
  • Debug編譯的文件大,Release生成的程序集小。
  • Debug會生成pdb文件,Release不生成pdb文件。

這些答案看上去好像都對,但是為什么Debug與Release有這么大的區(qū)別呢,這就需要深入的思考一下。

在Debug類與Trace類的區(qū)別一節(jié)中,相信有些朋友已經(jīng)明白,Debug編譯與Release編譯的區(qū)別其實是編譯配置的不同,DEBUG、TRACE常量的定義就是其中不同的地方。那么Debug編譯與Release編譯還有什么不同呢。針對這個問題,我總結(jié)了一下,主要區(qū)別有下面幾點:

  • 常量定義不同
  • 優(yōu)化代碼的不同
  • 包含的調(diào)試信息不同
  • 輸出路徑不同

接下來,我們詳細(xì)的看一下。

常量定義不同

這個相信大家已經(jīng)非常清楚了,Debug模式下會定義DEBUG、TRACE常量,而Release模式下只定義TRACE常量,如下圖所示

常量定義_Debug_Release對比

優(yōu)化代碼不同

debug_release_優(yōu)化代碼選項對比

Debug模式下,默認(rèn)不會進(jìn)行代碼的優(yōu)化,而Release模式下,由于默認(rèn)選中了“優(yōu)化代碼”選項,所以編譯器會對生成的代碼進(jìn)行優(yōu)化,以提高運行速度。

包含的調(diào)試信息不同

編譯生成的屬性頁中有一個“高級”按鈕,點擊后會彈出一個對話框,然后可以對編譯生成進(jìn)行一些設(shè)置,如下

生成高級選項

Debug與Release的高級選項對比:

debug_release_生成高級選項對比

從圖中看到,Debug模式下默認(rèn)會生成全部的調(diào)試信息,而Release模式下只生成pdb文件。

輸出路徑不同

這一點詳細(xì)大家都很清楚了,Debug模式下會輸出到Debug目錄,Release模式下會輸出到Release目錄,如圖:

debug_release_輸出目錄對比

Debug編譯與Release編譯的區(qū)別總結(jié)

說了這么多,我們再來思考本節(jié)開始時說到的關(guān)于Debug編譯與Release編譯的區(qū)別的答案,可以得出這樣的結(jié)論:

  • Release比Debug運行速度快,執(zhí)行效率高。由于Release模式編譯會對生成的代碼進(jìn)行優(yōu)化,所以會使生成的程序集在運行的時候比Debug編譯的程序集運行速度快。
  • Debug 是調(diào)試用的,可以打斷點執(zhí)行。Release是發(fā)布用的,不能打斷點執(zhí)行。由于Release模式對代碼進(jìn)行優(yōu)化,生成的調(diào)試信息只包含pdb文件,所以導(dǎo)致調(diào)試起來會比較困難。
  • Debug編譯的文件大,Release生成的程序集小。依然是由于Release模式對生成的代碼進(jìn)行了優(yōu)化,所以就導(dǎo)致生成的程序集相對來說比較小。
  • Debug會生成pdb文件,Release不生成pdb文件。這個要看VS的默認(rèn)選項,在2012中,Release模式默認(rèn)會生成pdb文件的,其他版本的VS可能會有不同的情況。

知道這些區(qū)別后,我們就可以通過修改編譯配置來使Debug程序達(dá)到Release的效果,使Release程序達(dá)到Debug的效果。當(dāng)然,還是那句話,雖然可以實現(xiàn)這樣的效果,但是建議絕對不要這樣做,不然程序維護(hù)起來,肯定會遇到很多問題。

總結(jié)的重要性

有人說,互聯(lián)網(wǎng)這么發(fā)達(dá),遇到什么問題直接谷歌百度,內(nèi)事不決問百度,外事不決問谷歌。但是即使你通過網(wǎng)絡(luò)解決了問題,如果不消化吸收,經(jīng)過總結(jié)變成自己的東西,那么下次還會遇到同樣的問題。書到用時方恨少,一回首,已白了少年頭。如果不總結(jié),任他虐我千百遍,我卻視他如初見,從網(wǎng)絡(luò)上找到的方法、代碼、解決方案,那是別人的知識,只有經(jīng)過實踐去驗證,通過總結(jié),消化吸收,才能將這些知識“據(jù)為己有”,為我所用。

那么至少從現(xiàn)在做起,把每天學(xué)到的知識,遇到的問題,得到的經(jīng)驗,總結(jié)下來,相信自己,這是大牛的節(jié)奏。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲国产综合久久天堂| 一区二区三区四区亚洲另类| 日本熟妇五十一区二区三区| 操白丝女孩在线观看免费高清| 精品欧美国产一二三区| 91欧美亚洲精品在线观看| 欧美三级不卡在线观线看| 亚洲国产成人久久一区二区三区| 国产专区亚洲专区久久| 亚洲人午夜精品射精日韩 | 国产成人高清精品尤物| 中文字幕亚洲人妻在线视频| 免费午夜福利不卡片在线 视频 | 五月天丁香亚洲综合网| 中文字幕五月婷婷免费| 视频一区中文字幕日韩| 国产人妻精品区一区二区三区| 亚洲欧美日韩熟女第一页| 国产av精品高清一区二区三区 | 国产精品午夜福利免费阅读| 樱井知香黑人一区二区| 激情亚洲内射一区二区三区| 伊人久久青草地综合婷婷| 欧美国产日产在线观看| 日本福利写真在线观看| 深夜福利亚洲高清性感| 国产av精品一区二区| 日韩欧美黄色一级视频| 国产午夜在线精品视频| 老鸭窝精彩从这里蔓延| 国产精品一区二区不卡中文| 国产精品熟女在线视频| 国产精品不卡高清在线观看| 搡老妇女老熟女一区二区| 91人妻人人揉人人澡人| 午夜激情视频一区二区| 日韩精品一区二区三区av在线| 亚洲精品成人福利在线| 欧美日韩综合在线第一页| 久久精品蜜桃一区二区av| 亚洲欧美日韩在线看片|