在C#.net中跟踪和调试程序

在尝试发现程序中错误的时候,我们是如何去调试的呢?我们常用的错误方式就是:添加许多中跟踪和调试语句,然后,一旦调试完毕,我们必须马上删除这些添加的语句.事实上,.net框架中早已经为我们提供了三个可以用于调试的类:
System.Diagnostics.Debug,System.Diagnostics.Trace,System.Diagnostics.EventLog。
前面两个类功能是基本上是一样的。不同的地方是,Debug只编译到Debug版本中,而Trace语句编译到Debug和Release版本中。EventLog类提供了一个入口,通过这个入口,你的程序可以写一些系统日志。EventLog类不支持运行时配置,但你可以把它封装到一个统一的简单接口中。

1.定义输出开关(TraceSwitch)

static   private  TraceSwitch librarySwitch  =   new   TraceSwitch(  " MySwitch " " The switch for this assembly "  );

第一个参数是开关显示的名字,第二个参数是描述。

2.定义配置文件
.Net框架使用一个应用程序配置文件来控制变化多样的运行时设置。这是一个XML文件,在主应用程序运行时的目录中。该文件的名字与程序的名字相同,扩展名为“.config”。例如,如果程序是myApp.exe,就建一个myApp.exe.config。所有的配置信息都包含在一个configuration节点中:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

</configuration>

.Net框架使用预定义的关键字来控制框架中一些类的行为。也可以自定义配置关键字和值。
也可以组合输出开关和Trace.WriteLineIf()方法来控制应用程序的输出。还可以在应用程序外以默认的方式关闭这个输出,以便使应用程序得到最好的性能。当发现bug时,你可以打开这个输出用于诊断和修正bug。

--1.配置输出开关
输出开关的值可以是五种状态之一:关闭(Off),错误(Error),警告(Warning),信息(Info)和详细(Verbose)。这些状态是环境的一部份,而且它们的值可以是从0到4。这样如果你编辑了这个配置文件中开关的值,那么就修改了所有由那个开关控制的输出语句。
在配置文件中写入以下语句就配置了一个名为MySwitch,值为info的输出开关.:

<system.diagnostics>
  <switches>
    <add name="MySwitch" value="3" />
  </switches>
</system.diagnostics>

--2.添加监听者
默认是一个链接到Trace类上的监听者:一个DefaultTraceListener对象。DefaultTraceListener发送信息到调试器,而且在它的失败方法(断言失败时调用)会打印一些诊断信息然后终止程序。在产品发布环境中,你不可能看到这样的信息。但你可以配置不同的监听对象到产品发布环境中:那就是在应用程序的配置文件中添加监听者。下面就添加了一个TextWriterTraceListener 到应用程序中:

<system.diagnostics>
  <trace autoflush="true" indentsize="1">
    <listeners>
      <add name="MyListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="MyListener.log"/>
    </listeners>
  </trace>
</system.diagnostics>

TextWriterTraceListener把所有的诊断信息到打印到一个MyListener.log文件中。name属性指定了监听者的名字,type指定了作者监听对象的类型,它必须是从System.Diagnostics.TraceListener派生下来的。initializeData的值是一个字符串,用于传给对象的构造函数。而TextWriterTraceListeners把它用于文件名。

3.为了调试,我们常常需要编写一些只在调试时用到的函数,对于调试函数,在发布(Release)版本中,编译器会自动取消对它的调用。

[Conditional ( " DEBUG " )]
private   void  ImOK ()
{
Debug.Assert(
this   !=   null , " Testing Object State " , " this cannot be null " );
//  More here.
}
// 调用方法: 
public   bool  ProcessIterations ( int  numIters)
{
ImOK ();
Debug.Assert(numIters 
>   0 , " ProcessIterations. " , " Iterations must be more than 0 " ); 
}

4.调试应用示例
--1.首先,写一个通用的类来跟踪程序:

internal   class  MyAssemblyDiagnostics
{
  
static   private  TraceSwitch myAssemblySwitch  =
    
new  TraceSwitch(  " MySwitch " " The switch for this assembly "  );

  
internal   static   void  Msg( TraceLevel l,  object  o )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level 
>=  l, o,  " MySwitch " );
  }

  
internal   static   void  Msg( TraceLevel l,  string  s )
  {
    Trace.WriteLineIf( myAssemblySwitch.Level 
>=  l, s,  " MySwitch " );
  }

  
//  Add additional output methods to suit.
}


--2.调用方法:

public   void  Method1( )
{
  MyAssemblyDiagnostics.Msg( TraceLevel.Info, 
" Entering Method1. "  );

  
bool  rVal  =  DoMoreWork( );

  
if ( rVal  ==   false  )
  {
    MyAssemblyDiagnostics.Msg( TraceLevel.Warning, 
" DoMoreWork Failed in Method1 "  );
  }

  MyAssemblyDiagnostics.Msg( TraceLevel.Info, 
" Exiting Method1. "  );
}

--3.进一步扩展类:
利用一个全局的开关,还可以组建特殊的程序集开关,来控制整个及个别库文件的输出:

internal   static   void  Msg( TraceLevel l,  object  o )
{
  Trace.WriteLineIf ( librarySwitch.Level 
>=  l  ||  globalSwitch.Level  >=  l, o,  " MyLibrary "  );
}

internal   static   void  Msg( TraceLevel l,  string  s )
{
  Trace.WriteLineIf( librarySwitch.Level 
>=  l  ||  globalSwitch.Level  >=  l, s,  " MyLibrary "  );
}

5.几点建议
在写一个函数时,你应该考虑并确定你对那个函数做了什么样的假设。你应该问自己以下这些问题,
并把答案放到代码中:
--1. 当这个函数被调用时,这个对象必须是怎样的(对象初试化,某个内在变量值)? 
--2. 当这个函数存在时,这个对象将会怎样(仍是#1,但包括该函数的副作用)?
--3. 该函数的任何参数必须是怎样的(允许空值吗,输入值的范围是什么)?
--4. 返回值必须是怎样的? 

为了发现程序中的bug,以下是一些较好的做法:
--1. 当你创建类时,通常为每个类建一个跟踪开关。 
--2. 通常为每个类建一个验证函数。 
--3. 当你要诊断错误的行为时,添加其它的跟踪和调试语句。确信把这些变化保留在代码中。 

參考文獻: http://tech.goodspeed.com.cn/120393.aspxhttp://support.microsoft.com/kb/815788/zh-cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值