c#的预编译指令

我的blog是用开源的BlogEngine来架设的,有的时候为了满足自己的需求及要对源代码做一些修改。在我调试客户端代码的时候,不管是使用Firebug或者是Vs 2008来调试,看到的Javascript代码都是经过动态压缩过了的,这个系统有一个HttpHanddle是专门用来处理js文件请求的,在第一次请求的时候会对js代码进行压缩,去掉了注释换行符等不必要的字符,这样可以提高访问的速度,但是对调试非常的不利,相信我们谁都不愿意对着一堆压缩过了的JS代码做调试。于是我想到了C#的预编译指令,再网上搜索整理了一番,做一个总结。

c#的预编译指令

C#预处理器指令是在编译时调用的。预处理器指令(preprocessor directive)告诉C#编译器要编译哪些代码,并指出如何处理特定的错误和警告。C#预处理器指令还可以告诉C#编辑器有关代码组织的信息。

1. 定义符号和取消符号定义的预处理指令#define 和 #undef

预处理指令都以#号开头并位于行首前面可以出现空格符。

纯文本 复制 打印
  1. #define DEBUG  
  2. #define ISSAY  

上面的语句定义了连个个预编译的符号,他的作用域是他所处整个文件,定义符号的语句必须出现在所有代码之前, 否则编译的时候会出现一个异常: 不能在文件的第一个标记之后,定义或取消定义预处理器符号 。我们也可以使用#undef来取消一个符号的定义,先来看个例子。

纯文本 复制 打印
  1. #define DEBUG  
  2. #undef DEBUG  
  3. #define ISSAY   
  4.   
  5. using System;   
  6. namespace JustDoIt   
  7. {   
  8.     class Program   
  9.     {   
  10.         static void Main(string[] args)   
  11.         {  
  12.             #if DEBUG   
  13.             Console.Write("debug.");  
  14.             #endif  
  15.             #if ISSAY   
  16.             Console.Write("hello.");  
  17.         #else   
  18.             Console.Write("you can say nothing.");  
  19.             #endif   
  20.   
  21.         Console.ReadLine();   
  22.         }   
  23.     }   
  24. }   
  25. //输出:hello  

从上面的代码我们可以看到第一样等一了符号DEEBU,紧接着第二行取消了这个符号的定义,也就是相当于没有定义一样,所以程序运行的时候不会执行Console.Write("debug.")这个语句。第三行定义了ISSAY符号,所以程序输出了“hello”,如果我们把他注释了或者是删除了,那么程序会输出“you can say nothing“。我们可以初步看到通过定义预编译的符号,可以控制编译器选择性地编译代码。上面的代码中还有#if和#endif这样的符号,这些是条件编译指令。

2. 条件编译指令

条件编译指令有4个,除了我们从第一个示例看到的#if#else#endif之外,还有一个#elif。我们对这些指令应该有是曾相识的感觉,他们跟我们平时编写代码的时候试用的条件语句是一样的,条件语句是用来控制程序流的,而这些条件编译指令是用来控制编译器选择性地编译代码的。

一条#if语句可以有0条或多条#elif语句,也可以有0条或一条#else 语句,但必须包括一条#endif语句必须有,否则会出现语法错误。

3. #region和#endregion

这两个符号平时我们肯定用的很多了,就是把一些相关的代码折叠到一起,这样对我们在一个文件中编写较长的代码非常有用,我们可以把一组相关的代码用#region和#endregion组织在一起并且可以在#region后面加上说明的文字,当这组代码被折叠起来的时候,我们可以看到#region后面的说明文字。

4. #warning、#error

这两个指令对编译器自身估计比较有用,如果我们用vs写代码的话,编译项目的时候有时就会看到错误列表窗口里列出出现的错误、警告或者消息的信息。

纯文本 复制 打印
  1. #define DEBUG   
  2.   
  3. using System;   
  4. namespace JustDoIt   
  5. {   
  6.     class Program   
  7.     {   
  8.         static void Main(string[] args)   
  9.         {  
  10.             #if DEBUG                                  
  11.             #warning "debug状态,js代码不会被压缩"  
  12.             #elif RELEASE                              
  13.             #warning "Release状态,js代码将会被压缩"  
  14.             #else  
  15.             #error "并清楚什么状态"  
  16.             #endif   
  17.             Console.ReadLine();   
  18.         }   
  19.     }   
  20. }  

以上的代码将不会通过编译,错误的信息就是”并清楚什么状态“,也就是#error后面的信息。如果把#error这行删除或者注释掉的话,那么我们在编译程序的时候可以在错误列表窗口中看到“debug状态,js代码不会被压缩”的警告信息。

5. #line ……

总结:以上的这些指令大概只有1、2、3对我们会比较有用,不过为了文章的完整我把在网上找到的其他的符号也加进来了。

然后回到这个blog的Javascript调试问题。在vs里面可以直接在代码里面使用#DEBUG符号,下面是我的截图。

我们可以看到#if (DEBUG)下面的那样是灰色的,就是如果这个时候编译代码的话,会编译#else下面的语句,因为这个时候我把生成的方式设置为了release(我们可以在项目或者是解决方案的属性里面配置项目生成的方式)。

如果我们把项目生成的方式设置为debug的话,那么执行的就是#if (DEBUG)下面的语句,这样就直接把js文件的路径赋给了控件的src属性,这样生成的html代码是一般的sript标签,不会对js文件进行压缩,这样在调试Javascript时候便可以看到格式良好的代码了。在以release方式编译代码的时候,调用了ResolveScript()方法来对这个url做了处理,生成格式换的url会直接调用js文件处理的HttpHanddle来对js代码进行压缩。

最近合作开发,代码已经完成了,但是一调试,错误一大堆,由于是合作开发,不确定是哪层的错误,得一步步得走,很是费时费力,平时调试的技巧用的不多,现在集中调试,结果有些手忙脚乱,效率也很低,所以在网上找了一些技巧,整理了一下,分享给大家。它们都很简单,却能帮我们节约大量的时间,希望对大家有帮助。

 

  跳到当前光标处(Ctrl+F10)

  我经常看到人们为了到达目标代码位置,而在程序中早早设定了断点,然后反复地按F10/F11,一步步走到目标代码处。当程序员的确需要仔细观察每一步的状态变化时, F10/F11是合理的选择。然而多数情况下,人们只想快速到达他们真正关心的代码处,这时候F10/F11就不是最佳选择了。

  这时,你应该利用“跳到当前光标处”这个功能。先把光标定位在要测的目标代码行上,再同时按Ctrl和F10,被测程序将直接跳到该行停下。你再也不用按许多次F10/F11了。即使目标代码位于独立的类或方法中,你仍然可以从当前正在检查的地方跳过去。

  条件中断

  另一种常见的情况是:开发人员设置断点,运行程序,利用不同的输入触发断点,然后在断点处手工检查是否满足某些特定的条件,从而决定是否继续调查。如果当前场景不是他们想要的,按F5继续运行程序,尝试别的输入,手动重复刚才的过程。

  针对上述情况,Visual Studio提供了一个方便得多的功能——“条件中断”。只有当程序满足了开发人员预设的条件后,条件断点才会被触发,调试器中断。这将避免频繁地手工检查/恢复程序运行,大量减少调试过程中的手工和烦琐工作。

  如何设置条件断点

  设置条件断点非常容易。在特定的行上,按F9设置断点。

\

  然后右击断点–编辑窗口左侧的红点,在上下文菜单上选择“Condition…”。

c#的预编译指令_第1张图片  这时弹出一个对话框供你设置激活该断点所需的条件。比如:我们希望只有当局部变量paginatedDinners的尺寸小于10时,调试才中断。我们可以写出如下的表达式:

c#的预编译指令_第2张图片  现在我再运行这个程序,实现搜索,只有返回值小于10时,程序运行才会被中断。对于大于10的值,该断点将被跳过。

  记录到达断点次数

  有时你希望,只有当第N次满足条件的运行到达断点时,才中断程序运行。例如:当第五次返回少于10份晚餐的查询结果时,中断程序运行。
  可以通过右击断点,然后在弹出菜单上选择“Hit count…”菜单命令实现。

c#的预编译指令_第3张图片

  这时系统弹出一个对话框,它允许你指定:(1)当满足条件,而且进入断点的累计次数等于N时,断点命中一次。(2)当满足条件,而且进入断点的累计次数是N的倍数时,断点命中一次。(3)当满足条件,而且进入断点的累计次数大于N时,每次命中断点。
c#的预编译指令_第4张图片

  机器/线程/进程过滤

  设置如下:右击断点;在弹出菜单上选择“Filter…”菜单命令;然后指定命中断点的特定条件:在指定的机器上、或指定的进程中、或指定的线程中。

  跟踪点—进入断点时的自定义操作

  许多人不知道“跟踪点(TrackPoints)”这个调试功能。“跟踪点“是种特殊的断点,当它被命中时,它会触发一系列自定义操作。如果你想观察程序的行为,而又不想中断调试的时候,这个功能尤其有用。

  我将用一个简单的控制台程序来演示如何使用“跟踪点”。如下是斐波那契数列的一个递归实现:

c#的预编译指令_第5张图片

  以上程序中,我们使用Console.WriteLine() 输出针对特定输入值生成的最终斐波那契数列。如果希望在调试器里观察操作中每一次递归运算后的数列而又不实际中断程序运行,该怎么办呢?“跟踪点”可以轻松实现。

  设置跟踪点

  你可以在特定的行上,按F9加跟踪点。然后
  右击断点,在上下文菜单中选择“When Hit…”:

c#的预编译指令_第6张图片

  在弹出对话框上,你可以设置命中该断点时,所触发的事件。

c#的预编译指令_第7张图片

  在上面例子中,我们设定一旦命中断点时就打印追踪信息。注意,我们已经把局部变量“x”的值,作为追踪信息的一部分输出。局部变量可以通过{变量名}语法输出。你还可以利用系统内置的命令($CALLER, $CALLSTACK, $FUNCTION等等),在追踪信息中输出常用的调试值。

  在上例中,我们同时选中了底端的“continue execution“选项,这说明我们不希望程序中断调试状态,而是继续运行。唯一的不同是:每次断点条件满足时,我们的自定义追踪信息都将被输出。

  现在当我们运行程序时,会发现自定义追踪信息自动显示在Visual Studio的“输出“窗口里。这让我们很容易看到程序的递归调用过程:

c#的预编译指令_第8张图片

  你也可以选择往应用程序中添加一个自定义追踪信息的监听器。这时追踪点的输出信息将通过它输出,而不是Visual Studio的“输出“窗口。

  跟踪点—运行自定义的宏

  上周,我在伦敦演讲时,有听众问道:当命中跟踪点时,能否自动输出所有的局部变量?

  Visual Studio中并没有这样的内置功能,但我们可以写一个自定义宏来实现,然后在命中跟踪点时调用该宏。这个的实现需要先打开Visual Studio的宏编辑器(工具->宏->宏IDE菜单命令),然后在项目资源管理器的MyMacros节点下选择一个模块或创建新模块(如:加个名为“UsefulThings”的模块),再把下面的VB宏代码贴到模块中并保存。

 
   
Sub DumpLocals() Dim outputWindow As EnvDTE.OutputWindow outputWindow = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object Dim currentStackFrame As EnvDTE.StackFrame currentStackFrame = DTE.Debugger.CurrentStackFrame outputWindow.ActivePane.OutputString(“*Dumping Local Variables*” + vbCrLf) For Each exp As EnvDTE.Expression In currentStackFrame.Locals outputWindow.ActivePane.OutputString(exp.Name + ” = ” +exp.Value.ToString() + vbCrLf) Next End Sub

 

  利用可视化调试窗体(DebuggerVisualizers)

举一个DataTable的DebuggerVisualizers的例子:

c#的预编译指令_第9张图片

当然如果是DateSet,可视化工具窗口的表可以选择DataSet所包含的所有表的信息


你可能感兴趣的:(c#的预编译指令)