C#調試入門篇

閱讀目錄html

    DebugView輸出信息到DebugView的幾種方式DebugView與日誌框架比較ConditionalAttribute詳解與條件編譯TRACE常量Debug類與Trace類的區別Debug、Debugger、Kernel32的聯繫配置TraceListenerDebug編譯與Release編譯的區別總結的重要性

DotNet程序的調試,是DotNet程序員必備的技能之一,開發出穩定的程序、解決程序的疑難雜症都須要很強大的調試能力。DotNet調試有不少方法和技巧。如今本文就介紹一下藉助DebugView工具進行調試的方法,以及由DebugView引伸出來的知識點。程序員

DebugView

DebugView是一個查看調試信息的很是棒的工具,支持Debug、Release模式編譯的程序,甚至支持內核程序,並且可以定製各類過濾條件,讓你只看到關心的輸出信息,並且能夠定製高亮顯示的內容等等,很是方便。編程

debugview

捕捉Release模式的Win32程序輸出的調試信息,須要選中Capture Global Win32選項:網絡

debugview_release

過濾與高亮功能

debugview_過濾與高亮

能夠經過include、exclude設置過濾條件,包含指定字符串的輸出信息將會被過濾。還能夠經過exclude條件過濾掉對應進程ID的調試信息。多個條件使用「;」分隔,並且支持「*」通配符。框架

遠程調試

DebugView支持遠程捕捉調試信息。首先在遠程機器上經過以下命令啓動DebugView:函數

Collapse
DebugView.exe /a /t /g /s

這樣,DebugView就會以服務的方式運行,以下圖:工具

debugview服務

而後在本地機器上啓動DebugView,並經過Connect鏈接到遠程機器的DebugView,當遠程機器中有調試信息輸出時,本地就會捕獲到,並展現出來:post

debugview_connect

輸出信息到DebugView的幾種方式

DebugView的一些功能是否是讓你心動了呢。俗話說心動不如行動,可是在行動以前,首先要知道C#如何將調試信息輸出到DebugView中。學習

經過編程輸出一些調試信息到DebugView中,一共有三種方式:測試

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

1、Debug.WriteLine

經過Debug.WriteLine能夠將調試信息寫入到DebugView中,以下:

Collapse
Debug.WriteLine("這是調試信息");

效果以下:

Debug_DebugView

不過此方式只能在Debug模式下有效。具體緣由下面會詳細介紹。

2、Debugger.Log

Debug.WriteLine已經很好用了,惟一的缺點就是在Release模式下無效。那麼在Release模式下就可使用Debugger.Log方法,示例以下:

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

3、Kernel32.dll中的OutputDebugString方法

作C++開發的應該知道能夠經過OutputDebugString這個API開實現輸出調試信息到DebugView中吧。那麼C++能作的,C#也能作。能夠經過PInvoke的方式引入此方法,這個API屬於Kernel32.dll,以下聲明:

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

而後就能夠經過調用此方法,將調試信息輸出到DebugView中。

DebugView與日誌框架比較

可能有人會說,DebugView能作的事情,我用log4Net,NLog等日誌框架也能作,爲何要用DebugView呢?

問的好,那麼我就根據平時使用DebugView的經驗來給你一個用DebugView的理由:

  • DebugView使用很是方便。相比於日誌框架龐大的體系,DebugView的使用可謂是十分的簡單方便。DebugView只有幾百K的大小,佔用空間幾乎能夠忽略不計。從官網下載後,直接運行exe,幾乎不須要任何配置就能夠正常使用。而相比於DebugView,日誌框架能夠算的上龐然大物。而當你從官網得到log4Net後,須要進行各類繁雜的配置。甚至你要花上幾天時間專門學習一下這套框架。由此能夠看出DebugView的使用實在是方便的不能再方便。
  • DebugView是可視化工具,支持各類過濾和高亮。DebugView能夠經過過濾條件來過濾不關心的信息,只顯示相關的調試信息。而日誌框架輸出的是文件等文本信息,這些信息會包含程序運行過程當中的全部信息,雖然能夠經過配置文件來指定只輸出哪一類信息,可是不如DebugView來的方便簡單。
  • DebugView能夠實時監視。DebugView中有「自動滾動」的功能,程序中輸出的調試信息,基本上瞬間就會在DebugView中展現出來,當因爲大量信息致使DebugView中的文本框滿了後,DebugView能夠經過自動滾動滾動條,讓你隨時均可以看到最新的一條信息,達到相似監視的效果。而日誌框架因爲其寫文本的特性,很難達到這種效果,即便能達到,相信也是須要對日誌框架至關清楚瞭解,才能完成這個效果。

這些理由應該足以讓你使用DebugView了吧。使用DebugView的理由確定還不止這些,若是你有更好的理由,還請分享出來。

固然,DebugView與日誌框架,每一個都有每一個的用途。經過DebugView的方式,只適合短暫的調試,而正式發佈的網站或者軟件,須要一套記錄程序長期以來的運行狀態的工具,那麼就非日誌框架莫屬了。因此DebugView與日誌框架,要在合適的地方,發揮他們最大的功效。

聲明

Log4Net等日誌框架,功能足夠強大,也足夠豐富,相信上面說到的DebugView的功能,也能夠經過日誌框架來實現。可是和DebugView比較起來,會相對複雜一些。因此上面說到的使用DebugView的理由是基於方便性的比較,DebugView有足夠的方便性來讓你選擇使用他。

ConditionalAttribute詳解與條件編譯

說到調試,那麼確定有開發人員遇到這種狀況,開發產品的時候,遇到一些問題,就在代碼中加入了大量的調試輸出信息,好比經過Console.WriteLine、MessageBox.Show或者經過Ilog.Log記錄日誌,甚至臨時改變邏輯來驗證邏輯的正確性等。通過這些調試信息的幫助,終於解決了產品的問題。但此時又遇到了新的問題,產品最終發佈的時候,確定是不能有這些調試信息的,但是已經加了這麼多調試信息,難道要所有刪除掉嗎。這顯然不是一個好辦法,這麼多代碼,手一抖,很容易就刪除了不相關的代碼,形成不可預估的後果。

作過C/C++開發的,能夠從各類跨平臺的開源庫中看到,一堆一堆的#if....#else....#endif,這就是條件編譯,這也是C/C++跨平臺所依賴的最基本的東西,在Linux系統,編譯這段代碼,在Windows系統又編譯那段代碼,最終實現了代碼級別的跨平臺。

那麼C#中有沒有相似的功能呢,答案固然是有,並且有兩種:

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

ConditionalAttribute特性

下面是ConditionalAttribute的構造函數:

Collapse
public ConditionalAttribute(
    string conditionString
)

構造函數中的參數conditionString,是一個區分大小寫的條件編譯符號的字符串。

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

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

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

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

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

C/C++中有#if..#else..#endif,C#中也有這些,他們都被稱爲預處理器。經過預約義的條件編譯符號,來控制編譯時編譯哪部分代碼。以下:

Collapse
複製代碼
public bool HasPermission(string userName)
{
#if DEBUG
    //Debug模式下,不須要作權限判斷,直接返回true
    return true;
#else
    //Release模式下,只有sa用戶纔有權限
    if (!string.IsNullOrEmpty(userName) && userName == "sa")
    {
        return true;
    }
    else
    {
        return false;
    }
#endif
}
複製代碼

預約義的Debug宏在什麼地方

說到條件編譯,是否是隻有DEBUG 和 RELEASE兩種狀況呢,若是是這種狀況的話,那也就是說DEBUG和RELEASE兩種狀況是定義好了的,俗話說就是「作死了」,這是做死的節奏啊。不做死就不會死,至少VS在這點上尚未做死。

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

既然是條件編譯,那麼就應該和編譯選項有關。咱們知道C#項目,有一個屬性頁,能夠設置不少編譯的選項,以下:

VS項目生成屬性頁

從圖中看到,條件編譯是用的DEBUG常量,或者稱爲DEBUG條件編譯符號,是在這個編譯生成選項中定義的,若是去掉這個定義,那麼編譯後的HasPermission方法就會根據用戶名進行權限檢查,程序中經過Debug.WriteLine輸出的調試信息也會輸出到DebugView中,也就至關於Release模式下的效果。

其實DEBUG常量與Debug、Release模式並沒有太大的關係,惟一的關係就是,VS生成的項目中,Debug模式下,默認會選中「定義DEBUG常量」,而Release模式下,默認不會選中。也就是說,Debug模式,默認會定義DEBUG常量,而Release不會,以下圖:

Debug_Release默認配置

既然DEBUG常量與Debug模式無本質上的關聯,那麼爲何說到Debug,就認爲DEBUG呢。道理其實很簡單,世上本無路,走的人多了,便成了路。原本這個DEBUG常量只是Debug模式下的默認預約義的常量,只是由於你們習慣了,而且對它的這種預約義還比較承認,時間久了,就天然而然認爲DEBUG就表明Debug模式。

雖然咱們能夠經過去掉DEBUG常量,來使條件編譯在Debug模式下達到Release模式的效果,可是建議最好不要這樣作,由於這就像是你們廣泛都承認的一個約定,若是你一反常態,不遵照這個約定,對於程序,編譯沒有問題,可是後續維護確定會至關麻煩,因此還請大俠手下留情。

使用自定義的編譯常量

DEBUG常量做爲一種廣泛的約定,最好不要打破。若是有除DEBUG外的條件編譯須要,可使用自定義的編譯常量。

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

  • 經過編譯生成屬性頁中的條件編譯輸入框,定義本身的編譯常量。
  • 在代碼中使用#define預處理,來定義編譯常量。

咱們能夠在條件編譯的輸入框中,定義本身的編譯常量,多個常量之間用分號「;」隔開,並且支持中文常量,以下:

自定義條件編譯符號

固然,咱們也能夠在代碼中經過#define預處理來定義,好比:

Collapse
#define 緣生夢
#define hbccdf

可是有一點須要注意,define定義常量必須在using 命名空間以前,不然會形成編譯錯誤。

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

引入條件編譯後,咱們能夠經過VS很快知道哪些代碼會被編譯:

VS_debug_release顏色對比

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

resharper_debug_release顏色對比

從圖中看出,release模式,因爲沒有定義DEBUG、緣生夢兩個常量,因此,調用Test、Debug.WriteLine方法的地方就會變暗,這樣很直觀就知道這些代碼不會被編譯。再次說明一個道理,神器對於開發有很大的幫助做用。

經過反編譯查看生成後的代碼

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

debug_release_反編譯代碼對比

從圖中的反編譯後的代碼能夠看出,知足條件的代碼會真正編譯到生成的程序集裏,而不知足的代碼則不會生成到程序集裏。

Conditional修飾的方法,會有方法的實現,可是沒有方法的調用。不適合在方法裏作一些變量的修改。

總結Conditional和條件編譯

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

TRACE常量

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

如今咱們知道DEBUG常量是用來控制調用Debug.WriteLine的語句是否被編譯。那麼TRACE常量呢。

相信不少人也用過System.Diagnostics.Trace類,由DEBUG常量與Debug有關,能夠想到TRACE常量與Trace有關,咱們來看一下MSDN對於Trace的定義。

從MSDN中,看到Trace類的定義以及能夠調用的方法與Debug相似,都有WriteLine方法。下面是Trace的WriteLine方法定義:

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

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

Debug類與Trace類的區別

到如今爲止,咱們漸漸的瞭解了Debug類與Trace類,他們均可以經過WriteLine方法輸出調試或跟蹤信息,從他們的定義和暴露的方法中,能夠看出他們很是類似,那麼他們有什麼區別呢。

有些人會根據使用的效果總結爲:Debug類只能在Debug模式下執行,在Release模式下無效果,而Trace類在Debug和Release模式下均可以執行。

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

但這是他們兩個真正的區別嗎。咱們一點一點來分析。

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

MSDN_Debug_Trace對比

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

常量定義_Debug_Release對比

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

但DEBUG與TRACE只是在默認的狀況下的定義。當改變這些定義後,上面的結論就再也不正確。

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

常量測試_Debug_Release對比

而後在Debug和Release模式下分別執行Debug.WriteLine方法和Trace.WriteLine方法,Debug.WriteLine方法只在Release模式下有效,而Trace.WriteLine方法只在Debug模式下有效。上面的結論再也不成立。

爲了更好的驗證一下咱們的結論,對他們進行反編譯:

反編譯_Debug_Trace.WriteLine對比

由圖中的反編譯代碼看到,除了關聯的條件編譯常量不一樣外,內部調用的方法均爲TraceInternal.WriteLine,實現徹底同樣。

那麼下面來總結一下Debug與Trace的區別:

  • Debug類關聯DEBUG常量,Trace類關聯TRACE常量。
  • 默認狀況下,VS建立的項目Debug模式會定義DEBUG與TRACE,而Release模式只定義TRACE,使Debug只在Debug模式有效,而Trace在Debug和Release模式均有效。
  • 能夠經過修改DEBUG、TRACE的默認定義,來改變Debug、Trace的默認行爲。可是建議最好不要這樣作,由於改變了這種默認的約定可能會出現意想不到的問題。

Debug、Debugger、Kernel32的聯繫

每一個人大腦的空間都是有限的,零散的知識很容易忘掉,輸出調試信息到DebugView中的三種方法,因爲關聯性不是很強,很容易會忘掉其中的一兩種,那麼既然實現相同的功能,他們之間有什麼關聯嗎。

這就要從Debug的MSDN文檔提及。

咱們知道Debug編譯的程序,運行的時候能夠經過Debug.WriteLine方法輸出調試i信息到DebugView,而MSDN中的解釋

Collapse
將後跟行結束符的消息寫入 Listeners 集合中的跟蹤偵聽器。

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

Debug類總體觀賞

從總體看上去,Debug類的每個方法都經過Conditional與DEBUG常量管理,也就是默認狀況下,Debug類的全部方法在Debug模式下均不會編譯。

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

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

再來看一下TraceInternal方法:

Collapse
複製代碼
public static void WriteLine(string message)
{
    foreach (TraceListener listener in Listeners)
    {
        listener.WriteLine(message);
        if (AutoFlush)
        {
            listener.Flush();
        }
    }
    return;
}
複製代碼

上面的代碼是精簡後的代碼。從代碼中看到會調用集合中的每個listener的WriteLine方法。

那麼這些listener的WriteLine又作了什麼呢:

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

原來TraceListener的WriteLine是抽象類的抽象方法,那麼咱們獲得的listener是具體類的一個抽象,至關於接口,這是微軟的一向作法,再繼續下去,就須要知道是哪些具體的TraceListener了。

繼續上面的線索,咱們是從Listeners屬性中獲取到的TraceListener,那麼就去看Listeners的get實現:

Collapse
複製代碼
public static TraceListenerCollection Listeners
{
    get
    {
        InitializeSettings();
        if (listeners == null)
        {
            lock (critSec)
            {
                if (listeners == null)
                {
                    SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
                    if (systemDiagnosticsSection != null)
                    {
                        //從配置文件獲取listener,可是因爲此處沒有設置配置文件,因此先不研究這個地方
                            listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
                    }
                    else
                    {
                        //這裏new了一個TraceListener的集合
                            listeners = new TraceListenerCollection();
                        //這裏咱們看到了TraceListener的具體實現類DefaultTraceListener
                        TraceListener listener = new DefaultTraceListener {
                            IndentLevel = indentLevel,
                            IndentSize = indentSize
                        };
                        listeners.Add(listener);
                    }
                }
            }
        }
        return listeners;
    }
}
複製代碼

從代碼中,找到了具體的實現類DefaultTraceListener,那麼就快點看看他的WriteLine方法吧,有點火燒眉毛了。

實際上,WriteLine方法會調用內部的Write方法:

Collapse
複製代碼
private void Write(string message, bool useLogFile)
{
    if (base.NeedIndent)
    {
         //寫縮進,實際是空格
          this.WriteIndent();
    }
    if ((message == null) || (message.Length <= 16384))
    {
         //輸出消息
          this.internalWrite(message);
    }
    else
    {
        //當消息很長時,會經過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);
    }
}
複製代碼

與輸出信息有關的有兩個地方,一個是調用internalWrite,另一個是WriteToLogFile。從WriteToLogFile是有執行條件的。感興趣的能夠研究一下。重點來看一下internalWrite方法。

Collapse
複製代碼
private void internalWrite(string message)
{
    if (Debugger.IsLogging())
    {
       //調用Debugger.Log方法,這個方法能夠輸出信息到DebugView中
        Debugger.Log(0, null, message);
    }
    else if (message == null)
    {
        SafeNativeMethods.OutputDebugString(string.Empty);
    }
    else
    {
       //調用Native方法
        SafeNativeMethods.OutputDebugString(message);
    }
}
複製代碼

internalWrite中調用了兩個比較重要的方法。其中Debugger.Log方法是否是很熟悉呢。咱們剛剛在上面總結了三種輸出調試信息到DebugView的方法,其中就包含了Debugger.Log,而Debug.WriteLine方法中,又會調用到Debugger.Log方法,這樣,這兩個方法就創建起了聯繫。

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

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

果真,這就是對kernel32.dll中的OutputDebugString的封裝調用,而這又是上面三種方法其中的一種。好了,這樣Debug、Debugger、OutputDebugString就全都聯繫到了一塊兒,他們之間有了聯繫,是否是就更容易記憶了。

配置TraceListener

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

Collapse
複製代碼
SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
if (systemDiagnosticsSection != null)
{
     //從配置文件獲取listener,可是因爲此處沒有設置配置文件,因此先不研究這個地方
    listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
}
複製代碼

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

關於配置文件具體的讀取和解析過程,本文就再也不詳細介紹了,感興趣的朋友能夠自行研究,或者等到個人後面博文詳細介紹。

那麼如今主要說一下如何經過配置文件來設置TraceListener,以下:

Collapse
複製代碼
<configuration>
   <system.diagnostics>
      <trace autoflush="true" indentsize="2">
         <listeners>
            <add name="myListener" type="System.Diagnostics.ConsoleTraceListener" />
         </listeners>
      </trace>
   </system.diagnostics>
</configuration>
複製代碼

一樣,有配置文件,那麼就能夠經過代碼來實現一樣的功能,以下:

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

Debug編譯與Release編譯的區別

Debug與Release的區別,這個問題,相信不少人都會有疑問,也會有不少人有本身的答案,我聽到過的答案有這些:

  • Release比Debug運行速度快,執行效率高。
  • Debug 是調試用的,能夠打斷點執行。Release是發佈用的,不能打斷點執行。
  • Debug編譯的文件大,Release生成的程序集小。
  • Debug會生成pdb文件,Release不生成pdb文件。

這些答案看上去好像都對,可是爲何Debug與Release有這麼大的區別呢,這就須要深刻的思考一下。

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

  • 常量定義不一樣
  • 優化代碼的不一樣
  • 包含的調試信息不一樣
  • 輸出路徑不一樣

接下來,咱們詳細的看一下。

常量定義不一樣

這個相信你們已經很是清楚了,Debug模式下會定義DEBUG、TRACE常量,而Release模式下只定義TRACE常量,以下圖所示

常量定義_Debug_Release對比

優化代碼不一樣

debug_release_優化代碼選項對比

Debug模式下,默認不會進行代碼的優化,而Release模式下,因爲默認選中了「優化代碼」選項,因此編譯器會對生成的代碼進行優化,以提升運行速度。

包含的調試信息不一樣

編譯生成的屬性頁中有一個「高級」按鈕,點擊後會彈出一個對話框,而後能夠對編譯生成進行一些設置,以下

生成高級選項

Debug與Release的高級選項對比:

debug_release_生成高級選項對比

從圖中看到,Debug模式下默認會生成所有的調試信息,而Release模式下只生成pdb文件。

輸出路徑不一樣

這一點詳細你們都很清楚了,Debug模式下會輸出到Debug目錄,Release模式下會輸出到Release目錄,如圖:

debug_release_輸出目錄對比

Debug編譯與Release編譯的區別總結

說了這麼多,咱們再來思考本節開始時說到的關於Debug編譯與Release編譯的區別的答案,能夠得出這樣的結論:

    • Release比Debug運行速度快,執行效率高。因爲Release模式編譯會對生成的代碼進行優化,因此會使生成的程序集在運行的時候比Debug編譯的程序集運行速度快。
    • Debug 是調試用的,能夠打斷點執行。Release是發佈用的,不能打斷點執行。因爲Release模式對代碼進行優化,生成的調試信息只包含pdb文件,因此致使調試起來會比較困難。
    • Debug編譯的文件大,Release生成的程序集小。依然是因爲Release模式對生成的代碼進行了優化,因此就致使生成的程序集相對來講比較小。
    • Debug會生成pdb文件,Release不生成pdb文件。這個要看VS的默認選項,在2012中,Release模式默認會生成pdb文件的,其餘版本的VS可能會有不一樣的狀況。

知道這些區別後,咱們就能夠經過修改編譯配置來使Debug程序達到Release的效果,使Release程序達到Debug的效果。固然,仍是那句話,雖然能夠實現這樣的效果,可是建議絕對不要這樣作,否則程序維護起來,確定會遇到不少問題。

總結的重要性

有人說,互聯網這麼發達,遇到什麼問題直接谷歌百度,內事不決問百度,外事不決問谷歌。可是即便你經過網絡解決了問題,若是不消化吸取,通過總結變成本身的東西,那麼下次還會遇到一樣的問題。書到用時方恨少,一回首,已白了少年頭。若是不總結,任他虐我千百遍,我卻視他如初見,從網絡上找到的方法、代碼、解決方案,那是別人的知識,只有通過實踐去驗證,經過總結,消化吸取,才能將這些知識「據爲己有」,爲我所用。

那麼至少從如今作起,把天天學到的知識,遇到的問題,獲得的經驗,總結下來,相信本身,這是大牛的節奏。

 
 
分類: C#, 軟件調試
相關文章
相關標籤/搜索