Debugging in .NET

 

摘要

编写高质量代码是每一个程序员梦寐以求的目标,很多程序员花在解决Bug上的时间甚至远远超过了编写代码的时间。国内的很多软件公司也开始越来越重视软件的质量,配备了专门的测试人员对软件质量进行全面的测试。随着软件规模越来越庞大,结构也越来越复杂,即使是发现和解决一个小小的Bug也会花耗大量的人力和时间。为了更好的防止和监控Bug的产生,一个优秀的程序员应该培养良好的编程习惯,在自己的程序中加入Debug代码,以便于捕捉和定位Bug。在.NET Framework中这一切都变得简单。

本文将介绍.NET Framework类库中的Debug类和Trace类的用途,并通过一些例子帮助大家掌握它们的用法。

目录

  • 引言
  • Trace Method
  • Trace Level
  • Trace Output
  • Trace vs. Debug
  • Assert
  • 小结
  • 更多信息

引言

软件开发的过程也是不断与Bug作斗争的过程。通过编写Unit Test,程序开发人员可以测试代码中是否有Bug,而一旦Unit Test失败,开发人员可以通过程序中的Debug代码更加方便的定位和调试Bug。

.NET Framework中的Trace类和Debug类几乎包含了编写Debug代码所需要用到的所有功能,下面将逐一为大家进行介绍。

Trace Method

Trace(跟踪)是一个非常有用的技术,它可以帮助我们跟踪和分享应用程序运行的所有细节,帮助我们获取有用的信息。在.NET类库中,Trace是一个静态类,不需要实例化可以直接使用。Trace提供了四个输出的方法:Write, WriteLine, WriteIf, WriteLineIf。WriteLine在输出的时候会自动换行,而WriteIf和WriteLineIf提供的条件判断功能。下面是一个最简单的Trace输出的例子:

//一段简单的例子
  
  
Trace.WriteLine( "字符串匹配查找成功!");
  
  
 
  
  
//直接使用WriteLineIf进行判断
  
  
Trace.WriteLineIf( IsShowTrace,"起始位置是:" + myIndex.ToString());
  
  
 
  
  
//使用if语句进行判断
  
  
if ( true == IsShowTrace )
  
  
{
  
  
          Trace.WriteLine( IsShowTrace,"起始位置是:" + myIndex.ToString());
  
  
}
  
  

在上面这段简单的例子中,根据IsShowTrace是否为true来决定是否输出myIndex的值。

Trace Output

如果在命令行运行刚刚的例子程序,Trace的输出定向到命令行窗口。如果是在VS.NET中Debug刚刚的例子程序,Trace的输出定向到Output窗口。.NET Framework一共提供了3个Trace Listeners,用于Trace输出的重定向:DefaultTraceListener, EventLogTraceListener和TextWriterTraceListener。默认情况下使用的是DefaultTraceListener。我们也可以使用TextWriterTraceListener将Trace的输出定向到指定的文本文件中,使用EventLogTraceListener将Trace的输出定向到指定类型的系统事件Log记录中(仅在WINNT,Windows2000,WindowsXP以及.NET Server中支持)。可以在程序中或者Config文件中更改Trace Listener,请看下面的例子:

// 演示如何使用Listener
  
  
 
  
  
using System;
  
  
using System.Diagnostics;
  
  
 
  
  
namespace Listener
  
  
{
  
  
    class Application
  
  
    {
  
  
        [STAThread]
  
  
        static void Main(string[] args)
  
  
        {
  
  
            Trace.WriteLine("Trace listener 测试");
  
  
        }
  
  
    }
  
  
}
  
  

下面的config文件中,移去缺省的Listener,添加了一个EventLogTraceListener和一个TextWriterTraceListener:

<?xml version="1.0" encoding="utf-8" ?>
  
  
<configuration>
  
  
<system.diagnostics>
  
  
   <trace autoflush="true" indentsize="4">
  
  
      <listeners> 
  
  
         <remove type="System.Diagnostics.DefaultTraceListener"/> 
  
  
         <add name="myListener" type="System.Diagnostics.TextWriterTraceListener"
  
  
           initializeData="myListener.log" />
  
  
         <add name="myListenerEventLog" type="System.Diagnostics.EventLogTraceListener"
  
  
          initializeData="Application" />
  
  
      </listeners>
  
  
   </trace>
  
  
</system.diagnostics>
  
  
</configuration>
  
  

执行上面的例子,在myListener.log文件中可以看到:

Trace listener 测试

打开事件查看器,在Application Log中可以看到:

这样,你可以通过更改Trace的Listener将Trace信息输出到指定的地方。

Tracing Level

在整个团队进行软件开发时,过多的Trace会令人无所适从,同时也对程序的性能造成一定的影响。同时,Trace输出的所有内容在最终的发布版本中同样有效,过多的输出内容只会使用户感到迷惑。因此,你可以通过Switches来指定Trace的Level,根据Switches的等级来决定Trace的输出。请看下面的例子:

using System;
  
  
using System.Diagnostics;
  
  
 
  
  
namespace Switching
  
  
{
  
  
          class SampleClass
  
  
          {
  
  
                    // 建立一个switch,其值由config文件进行配置
  
  
                    static TraceSwitch generalSwitch = new TraceSwitch("CoolSwitch", "Global scope");
  
  
 
  
  
                    static public void SampleMethod() 
  
  
                    {
  
  
                               //如果switch state设置为TraceError,输出该消息
  
  
                               Trace.WriteLineIf(generalSwitch.TraceError,"TraceError message");
  
  
                               
  
  
                               //如果switch state设置为TraceWarning,输出该消息
  
  
                               Trace.WriteLineIf(generalSwitch.TraceWarning,"TraceWarning message");
  
  
                               
  
  
                               //如果switch state设置为TraceInfo,输出该消息
  
  
                               Trace.WriteLineIf(generalSwitch.TraceInfo,"TraceInfo message");
  
  
                               
  
  
                               //如果switch state设置为TraceVerbose,输出该消息
  
  
                               Trace.WriteLineIf(generalSwitch.TraceVerbose,"TraceVerbose message");
  
  
                    
  
  
                    }
  
  
 
  
  
                    public static void Main(string[] args) 
  
  
                    {
  
  
                               SampleMethod();
  
  
                    }
  
  
          }
  
  
}
  
  

TraceSwitch有四个属性: TraceError,TraceInfo,TraceVerbose和TraceWarning。如果Switch的状态高于或等于相应的属性级别,则相应的属性值就为true,反之为false。例如,当Switch的状态值为2时, TraceError和TraceWarning属性为true。下面是Switch状态值的对照表:

Switch的值可以在Config文件中进行指定:

<?xml version="1.0" encoding="utf-8" ?>
  
  
<configuration>
  
  
<system.diagnostics>
  
  
   <switches>
  
  
      <add name="CoolSwitch" value="2" /> 
  
  
   </ switches >
  
  
</system.diagnostics>
  
  
</configuration>
  
  

运行上面的例子代码,输出为:

TraceError message
  
  
TraceWarning message
  
  

可以将Switche的Level设得高一些,当出现Bug时,在配置文件中将Switche的Level降低,以获取更多的信息。

Trace vs. Debug

Trace类中的方法和Debug类中的方法一模一样。那么究竟应该使用Trace类还是使用Debug类呢?或者什么时候使用Trace类什么时候使用Debug类?

最简单的区别就是Debug类的所有方法只在Debug版本中执行,在最终发布的Release版本不会有任何的Debug代码执行。而Trace类的方法在Debug和Release版本都会执行(当然,你也可以手动修改VS.NET默认的配置,但是没有任何的意义)。尽管两个类都提供了相同的方法,我还是建议限制Trace类的使用,将Trace类的使用限定在确实需要对代码进行跟踪的地方,否则将影响最终发布版本的性能。同时,最好对每一个类(源文件)定义单独的TraceSwitch,以便于在调整Trace Switch Level时尽可能减小对整个产品性能的影响。

总体说来,建议您在向系统的事件日志或者产品的Log文件进行记录时使用Trace类,因为这些记录的功能在产品发布时仍然需要(可以将缺省Trace Switch的Level设得高一些),而对于程序调试、捕捉Bug的Assert代码,则建议您使用Debug类。当然,您也可以根据自己的实际需要进行决定,但是一旦定下来就要遵循统一的标准,保持代码的一致性。

Assert

Assert是写Debug代码时最常用到的。编写高质量代码的准则之一就是不要有任何假设。凡是有"假设"的地方,都应该使用Assert进行判断。如上所述,我推荐您使用Debug类的Assert方法。

Debug使用的也是Trace的Listener,在缺省情况下Debug类的Assert方法将信息输出到MessgeBox中,例如:

public bool FindCustomer (int customerID)
  
  
{
  
  
          Debug.Assert (customerID > 0, "FindCustomer出错 ","customerID应该大于0"); 
  
  
          // More code...
  
  
          return true;
  
  
}
  
  

当调用FindCustomer方法,传入的参数customerID小于0时,会弹出Assertion Failed的MessageBox:

用户可以选择退出、重试,或者忽略。

任何一个失败了的Assertion都应该当作Bug来处理。需要注意的是,不要将Assert和Exception的处理(Try…Catch…Finally)混淆,Assert处理的是一些不应该发生的错误情况,而Exception处理的是由于各种原因(如网络环境,服务器Down机,数据库Crash等等)产生的异常。

一般来说,Assert可以用于以下几种情况:

1. 调用参数有效性

2. 返回值有效性

3. 数据有效性

4. 算法有效性

上面的例子就是对调用参数的有效性进行了检查,如果调用FindCustomer方法传入的参数customerID小于0,相应的Assert就会失败。下面是一个检验返回值有效性的例子:

if ( SetCustomerValid(customerID))
  
  
{
  
  
          Debug.Assert(CustomerIsValid(customerID));
  
  
          UserCustomer(customerID);
  
  
}
  
  
else
  
  
{
  
  
          Debug.Assert( !CustomerIsValid(customerID));
  
  
          ReleaseCustomer(customerID);
  
  
}
  
  

此外,Assert也可以用来检查一个数据结构或者对象的有效性、完整性,例如,下面的例子在调用一个public的方法时首先调用ImOK方法检查这个对象自身的完整性:

[Conditional ("DEBUG")]
  
  
private void ImOK ()
  
  
{
  
  
          Debug.Assert (this != null,"测试对象状态", "this 不能为 null");
  
  
          // More here.
  
  
}
  
  
 
  
  
public bool SetCustomerValid (int customerID)
  
  
{
  
  
          ImOK ();
  
  
          Debug.Assert (customerID > 0, "FindCustomer出错 ","customerID应该大于0"); 
  
  
          // More code...
  
  
          return true; 
  
  
}
  
  

当设计或实施一个算法时,也要保证该算法的正确性。例如,在设计快速排序算法时,可以将两种不同算法的排序结果相比较,如果不同则说明其中一种出错,Assertion失败。或者预先设定一组初始值,将计算结果与预测结果相比较。关于算法的正确性的验证这里不再举例。

小结

本文简要介绍了.NET Framework中Trace类和Debug类的用法,并给出了具体的例子代码帮助读者理解。对于一名优秀的程序员,编写Debug代码是安身立命的根本技能之一。希望本文能对大家有所帮助,提高编程的效率,开发出高质量的软件。

更多信息

请访问以下链接获取更多信息:

1. .NET Framework Class Library: Trace Class

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDiagnosticsTraceClassTopic.asp

2. .NET Framework Class Library: Debug Class

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticsdebugclasstopic.asp

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值