C#成神之路<15> C#异常处理调试

1、概述
bug分为三类:语法错误,语义错误,逻辑错误。

2、程序错误
程序错误:用户犯的错误。

(1)数据有效性检验
最常见的程序错误类型发生在用户向程序提供了输入数据,但没有输入正确的类型的时候。无效的数据输入可能是最常见的程序错误来源。(程序员不能控制用户向文本框对象中输入正确的内容)
有效性检验的几个方面:
I.类型检验:trypharse。
II.一致性检验:将两个或者两个以上的输入作比较,以确保输入数据的一致性。
III.范围减产:通过检验输入是否在合理值的范围之内,来验证输入数据是否合理。
IV.长度检验:经常要求用特定数目的字符作为输入。
中心思想:用户输入都是错的,用户都是傻逼。

(2)限制用户的输入
从给定列表中进行选择的程序,常见的输入技术是以列表框或者组合框对象的形式提供一个选项列表。
I.单选按钮
单选按钮对象:radioButton。
强制用户输入特定对象。单选按钮对象是与互斥选择关联。将每组相关的单选按钮选项放在各自的GroupBox对象中。这样就可以避免相关的选项只能够选择一个,互相冲突的问题。
一下是正常处理单选按钮选择的代码:

int choice;
if(rbMale.Checked==true)
{
chioce=MALE;
}
else
{
choice=FEMALE;
}

rb作为单选按钮的前缀。如果选项列表大于2,那么可以利用嵌套if来解决关于值的设定。
一般来讲单选按钮是有默认选项的。(比如多数用户是女性)那么代码可以如下设计:

public frmMain()
{
InitializeComponent();
rbFemale.Checked=true;
}

InitializeComponent()方法是用来构建窗体的内存映像,当控制从方法调用中返回时,在内存中构建了完整的窗体对象。如果修改窗体对象的默认状态,应该在构造函数当中进行修改。

II.复选框
另一种对用户输入进行限制的是利用CheckBox对象。
复选框选项不是互斥的,用户可以自由选择合适的多个选项。吧CheckBox存放在GroupBox对象中时不必要的。ckb作为复选框对象的前缀,如下是作为复选框的程序结构。

Array.Clear(toppings,0,toppings,Length);
//数组的固定清理方法。
if(ckbMushroom.Checked==true)
{
toppings[MUSHROOMS]=1;
}
if(ckbOlives.Checked==true)
{
toppings[OLIVES]=1;
}
if(ckbSausage==true)
{
toppings[SAUSAGE]=1;
}

该方法使用数组特定的Clear方法使得数组的所有元素都初始化为0。

III.组合框
组合框类似于列表框,只是用户也可以输入一个相应。(comobo box)
利用Property(属性)Items向列表中添加选项。
很多情况下需要用一个运行时确定的列表填充组合框。(从一个数据库中读取一个名称列表,希望将这些名称的一个子集,放到一个组合框中。)一般用cmb开头来描述相关对象的名称。

cmbSize.Items.Add("Small");
cmbSize.Items.Add("Medium");
cmbSize.Items.Add("Large");

逐字添加太费时间,可以利用数组和循环的结合玩成动态添加相关的条目。

int i;
for(int i=0;i<names.Lenth;i++)
{
cmbNames.Items.Add(names[i]);
}
cmbNmaes.SelectedInedex=0;

该组合框中的项列表是基于0的列表,所以前面的代码在列表中显示了第一项。如果没有设置SelectedIndex属性,那么组合框就是空的,等待输入。
注意:也可以让对象将添加到组合框当中的项列表的内容进行排序,从而使得用户更加容易定位到要从列表中选择的内容。添加到组合框中的项顺序对后面的代码会有影响。
在组合框中检索文本可以使用如下代码:

string buff=cmbPeople.Text;

从而获得相关的文本文件。
使用或者不适用带用户输入的组合框:可以在属性窗口中,将DropDownStyle属性设置为DropDownList,可以禁止用户向组合框中输入值。这种风格使得组合框成为只读对象。(只可以选择组合框中已有的选项,而不能自由输入。)

IV.日期与时间输入
另一个常见的输入是日期。DateTimePicker对象可以标准化这样的信息。
对象的状态Format可以设置为Long/Short。ShortUpDown属性可以设置成true/false。
要从DateTimePicker对象中提取信息,需要用到如下代码:
注意,时间日期对象前缀为dtp。

string date =dtpDate.Value.ToShortDateString();
string time=dptTime.Value.ToShortTimeString();

3、异常处理
异常:不希望的程序状态。并不是bug。

try-catch程序块
程序语法:

try
{
//程序语句
}
catch[(exception)]
{
//catch程序块
}
[finally
{
//finally程序块
}
]

try程序块的大括号中,是一个您认为可能会导致程序生成或者跑出异常的程序语句。程序员术语:“此代码会抛出异常”。
catch程序块的作用是:如果抛出异常的话,执行一个或多个语句。
可选的finally语句块表示无论是否跑出异常都要执行的代码块。(不是必须的)
程序一旦发生异常,程序控制就会被传递给catch语句块,再也不会自动返回到try中。

(1)预期特定的异常
调试->异常,这个命令可以看到异常列表。从而预期所发生的错误。利用如下代码具有针对性的指出错误:

catch(DivideByZeroException)

因为精确地估计到了错误,所以会向用户发出一条更加有意义的错误报告。同时可以提供多个catch模块,进行多个异常检测。

catch()
{}

如上的代码块未指定异常,对用户没有多大的帮助。

(2)模糊异常消息
示例代码:

try
{
result=exp1/exp2;
}
catch(DivideByZeroException)
{
MessageBox.Show("XXX","xxx");
txtExpressional.Focus();
return;
}
catch(Exception ex)
{
MessageBox.Show("Error: "+ex.Message,"Excption throw");
}

这段代码中定义了一个名为ex的异常对象变量。(消息框中使用Exception对象的Message属性提供发生已成更精确的消息,即“模糊异常消息”)

测试模糊异常
可以用throw关键字抛出一个异常来测试该异常,如下:

try
{
throw new ArgumentOutOfRangeException();
result=exp1/exp2;
}
catch(DivideByZeroException)
{
MessageBox.Show("XXX","xxx");
txtExpressional.Focus();
return;
}
catch(Exception ex)
{
MessageBox.Show("Error: "+ex.Message,"Excption throw");
}

try语句的第一个语句。ArgumentOutOfRangeException()异常以为这程序打算构造一个ArgumentOutOfRangeException,然后抛出该异常。
抛出:程序在执行构造函数时产生该异常。

ex.Message属性并不是Exception对象的唯一有用属性,也可以使用StackTrance属性来显示一直到发生异常时调用的方法。这些方法是按照逆序显示的。最后调用的方法将会列在列表的最上方。

4、程序调试(如何有效使用程序调试器)

(1)自然法则
修复程序错误三个步骤:检测、隔离、纠正。
I.检测
测试数据集:程序检测的关键。
应该获得一组已知的输入,并且手工处理这组输入,然后记录处理步骤中的数据,其结果成为输入步骤,将这些手工生成的测试结果作为衡量程序输出的标准。
程序bug一般在边界条件的位置出现。边界条件是位于测试数据集的极值上的条件。
并且测试没调代码的路径。
可以利用错误日志的方法记录程序错误,在catch模块中调用相应的错误日志方法,可以将相应的程序值写入到硬盘中。

II.隔离
有一个可重复的bug,那么删除它之前要隔离他。

(2)VS调试器
优点:辅助隔离程序bug。隔离bug的起始点是断点。(执行程序时希望程序暂停的地方。)
Local窗口前文已经叙述,此处不再多说。

Immediate窗口
Debug->Windows->Immediate命令的方式激活。Immediate窗口的用途是可以再其中输入一个表达式并分析其对代码的影响。(立即修改一个局部变量的值并且估计他对代码行为的影响)

Watch窗口
Debug->Windows->Watch命令调用出窗口。该窗口可以来计算表达式甚至使用当前不在作用域中,但是在其他某个断点处可能在作用域中的变量。

(3)单步调试程序

左边空白部分显示了黄色箭头以及一条消息,表示可以将光标托槽前面一行上以再次执行那一行。

调试工具栏
Go:直接导致程序从断点处按正常速度恢复执行。

Break All:提供了一个暂停使得您可以在程序执行的时候手工进入调试模式。

Stop:终止程序的当前运行状态。

Restart:用于简单的重启程序。

Show Next:显示了要执行的下一个语句。

Step Into 单击Step Into按钮,如果要执行的语句是一个其源代码可用的方法调用,那么程序执行会前进到那个方法的源代码处。

Step Over:单击Step Over 按钮,如果要执行的语句是一个其源代码可用的方法的调用,那么程序执行会跳过该方法的源代码,并且继续执行当前语句后面的下一个语句。

Step Out:单步进入了一个方法的源代码,当通过一半时,决定返回方法的被调用处。

(4)脚手架代码
脚手架代码:基于预处理器指令添加或删除的调试代码被称为脚手架代码。
C#中允许使用的预处理器命令:

#define DEBUG

#if DEBUG
   MessageBox.Show("Value of exp1="+exp1.tostring());
 #endif

#define预处理指令必须是源代码文件的第一行。(类作用域)

固定脚手架代码
将脚手架代码钉入和钉出程序的最容易的方式是在源代码文件的嘴上发使用如下两行代码:

 #define DEBUG
 //#undef DEBUG

(5)防御性编码
防御性代码:意味着按照比较容易调试和维护程序的方法编写代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值