F#入门-第二章 F#基础-第十七节 异常(一)
2010-09-03 17:11
246 查看
■ 关于异常
正式编写程序的时候,不仅要编写能够正常执行的程序,还要编写当错误发生时能够对该错误进行处理的程序。
因此,编写程序者不仅要编写错误处理程序,还要使用F#的异常处理机制来使得错误处理能够得到统一。
再介绍异常处理之前,首先,请先注意如下事项。
注意事项
而且,异常是很容易引起的。
由于除0所引起的异常
在这个示例中,由于执行了除以零的除法运算,引起了System.DivideByZeroException错误,由于未对该错误做任何处理,所以程序终止.虽然这个程序即使立刻终止了也不会产生任何问题,但如果我们努力做成的应用程序在运行中途被立刻终止了,那就很悲哀了。
为了避免发生这种事情,我们要
1 努力针对可能发生的异常进行捕捉及处理。
2 尽量知道在什么情况下会发生异常。
例如,如果使用.Net Framework,在MSDN中,记载着各个类的方法会抛出什么样的异常。就连类的构造器也可能抛出异常,所以有必要对这些异常进行仔细确认。实际上,在进行程序编写的时候,如果不能具体确认会抛出哪些异常,最低限度也要确认使用到的函数或方法是否会抛出异常。
另外,不管是F#的源语言OCaml也好,.Net Framework也好,都有异常处理机制。F#的异常处理机制更偏向于哪一种?下面来详细介绍。
■ 异常的基本处理
在F#中异常为exn类型,exn类型为.Net中的System.Exception类的简便写法。
在.Net中考虑使用异常的时候,通常不使用Exception类,而使用继承了Exception的派生类。(关于继承,请参考“面向对象”一节)
在F#中异常为exn类型,exn类型为.Net中的System.Exception类的简便写法。
但是,在F#中,用Discriminated Union来定义异常。(关于Discriminated Union,请参考“模式匹配”一节)。也就是说,如果说F#的异常处理机制更偏向于哪一方的话,相比较.Net Framework而言,F#是更偏向于Ocaml的.
使用exception关键字来定义异常.
异常的定义
上面是Siyou_desuException的定义(名字是随便起的,可以起任何名字.)。下面一行中生成的值为,实际抛出异常的时候使用的值。
事实上,为了引发异常,可以编写如下代码。
使用raise来抛出异常。
因为在解释器中会弹出错误,所以使用编译器,执行结果如下。
使用raise来抛出异常
执行结果
另外,除了raise之外,F#中还定义了许多使用起来很方便的函数用来抛出异常。
failwith 字符串
用来抛出FailureException异常。(在F#中的简略形式为Failure)
例如:failwith "失败了";;
invalid_arg 字符串
用来抛出InvalidArgumentException异常。(在F#中的简略形式为Invalid_arg)
例如:invalid_arg "aaaaaaaaaa";;
这些函数的类型如下所示。
作为共同特征的是,由于这些函数都不必要正常结束(因为产生了异常),所以->右侧的具体类型没有被决定,而是用a这种模糊类型来代替。
与异常有关的函数的类型
接下来介绍异常捕捉。
异常的捕捉采用如下的表达方法。
try-catch 表达式try
表达式
with
| 匹配模式1 -> 表达式2
| 匹配模式2 -> 表达式3
...
try-finally 表达式try 表达式1 finally 表达式2
在try-catch表达式中,如果在try表达式中引发异常,会依次将每个匹配模式与异常进行比较,并且,对于第一个匹配模式,将会执行该分支的对应表达式(称为“异常处理程序”),并且总体表达式将在该异常处理程序中返回该表达式的值。(在F#中称为try-with表达式)
第二段try-finally表达式为,无论表达式1中是否引发异常,都会执行表达式2中的代码,不捕捉异常。
另外,在其他语言中也有try-catch-finally这种三阶段的异常处理方法,但是在F#中并不支持该异常处理方法。(在OCaml中也不支持)。
应该注意的一点是,如果使用try-finally表达式,则必须在该表达式之外捕捉并处理表达式中可能引发的异常,否则程序终止。
接下来,我们来看实际的异常捕捉示例。
异常捕捉(try-catch表达式)
执行这段代码时如果充值卡上余额不满10元时辉显示“余额小于10元”。
另外,当不符合上面的几种情况时,为了抛出Invalid_arg异常时可以使用rethrow()函数重新抛出异常。rethrow函数是让捕捉到的异常重新抛出的函数。
下面是使用finally的示例。
异常捕捉(try-finally表达式)
这段代码中,执行1/0时由于除0运算引发了异常。finally并不捕捉并处理异常,所以程序异常终止。
但是,因为finally后的代码会被执行,所以在运行结果中仍然会显示字符串。
上例中程序的执行结果
在这个示例中,有必要在别的地方对该异常进行捕捉。
基本上在如下场合中使用finally。
1.确保得到了内存等资源的释放。
2.即使引发了异常也要执行如下代码。
3.确保内存等资源的释放。
(语句2放在try表达式中,语句3放在finally表达式中)
如果不执行语句3,就会引起资源得不到释放或内存泄漏等情况.
try-finally示例2
在这段代码中生成了之记载文字"0"(ASCII代码为48)的文件a.txt。使用finally来调出fs.Dispose()函数。Dispose为具有“进行善后处理”意义的函数,在.Net中凡是继承了System.IDisposable接口的类都具有这个方法来释放资源。
但是,在这种情况下用use语句会使代码更加简洁明了。
与上述语句相同作用的代码
use语句的作用为,对in语句段中的代码执行try操作,用finally语句结束并在finally语句中如果fs不为null,则调用Dispose方法。但是如果要在finally语句中除了调用Dispose方法以外,还要进行别的处理,就必须自己写finally语句了。另外,要注意的是,上述代码中只保证调用了Dispose方法,并没有对异常进行捕捉。例如,如果将a.txt文件改为只读属性,再次执行上述代码,就会引起异常,程序崩溃,可以自己动手进行这个试验。
正式编写程序的时候,不仅要编写能够正常执行的程序,还要编写当错误发生时能够对该错误进行处理的程序。
因此,编写程序者不仅要编写错误处理程序,还要使用F#的异常处理机制来使得错误处理能够得到统一。
再介绍异常处理之前,首先,请先注意如下事项。
注意事项
发生异常时如果不捕捉并进行处理,程序立刻终止。 |
由于除0所引起的异常
> 1/0;; System.DivideByZeroException: 试图除以零。 位置 <StartupCode$FSI_0004>.$FSI_0004._main() stopped due to error |
为了避免发生这种事情,我们要
1 努力针对可能发生的异常进行捕捉及处理。
2 尽量知道在什么情况下会发生异常。
例如,如果使用.Net Framework,在MSDN中,记载着各个类的方法会抛出什么样的异常。就连类的构造器也可能抛出异常,所以有必要对这些异常进行仔细确认。实际上,在进行程序编写的时候,如果不能具体确认会抛出哪些异常,最低限度也要确认使用到的函数或方法是否会抛出异常。
另外,不管是F#的源语言OCaml也好,.Net Framework也好,都有异常处理机制。F#的异常处理机制更偏向于哪一种?下面来详细介绍。
■ 异常的基本处理
在F#中异常为exn类型,exn类型为.Net中的System.Exception类的简便写法。
在.Net中考虑使用异常的时候,通常不使用Exception类,而使用继承了Exception的派生类。(关于继承,请参考“面向对象”一节)
在F#中异常为exn类型,exn类型为.Net中的System.Exception类的简便写法。
但是,在F#中,用Discriminated Union来定义异常。(关于Discriminated Union,请参考“模式匹配”一节)。也就是说,如果说F#的异常处理机制更偏向于哪一方的话,相比较.Net Framework而言,F#是更偏向于Ocaml的.
使用exception关键字来定义异常.
异常的定义
//异常的定义。请注意如果异常名的开头字母为小写的话是错误的。 > exception Siyou_desu of string;; exception Siyou_desu of string //没有参数也是OK的 //> exception Siyou_desu;; //之后,就可以作为普通的Discriminated Union来使用了。 > Siyou_desu "easter egg";; val it : exn = Siyou_desuException () |
事实上,为了引发异常,可以编写如下代码。
使用raise来抛出异常。
因为在解释器中会弹出错误,所以使用编译器,执行结果如下。
使用raise来抛出异常
exception Siyou_desu of string;; raise (Siyou_desu "EasterEgg");; |
执行结果
//因为抛出了异常,所以会出现如下对话框 未被捕捉到的异常: Program+Siyou_desuException: EasterEgg 位置 Microsoft.FSharp.Core.Operators.raise[A](Exception exn) 位置 <StartupCode$excp>.$Program._main() 位置 C:/Documents and Settings/username/My D ocuments/Visual Studio 2008/Projects/excp/Program.fs:行 4 按任意键继续. . . |
failwith 字符串
用来抛出FailureException异常。(在F#中的简略形式为Failure)
例如:failwith "失败了";;
invalid_arg 字符串
用来抛出InvalidArgumentException异常。(在F#中的简略形式为Invalid_arg)
例如:invalid_arg "aaaaaaaaaa";;
这些函数的类型如下所示。
作为共同特征的是,由于这些函数都不必要正常结束(因为产生了异常),所以->右侧的具体类型没有被决定,而是用a这种模糊类型来代替。
与异常有关的函数的类型
> raise;; val it : (System.Exception -> 'a) = <fun:clo@0_5> > failwith;; val it : (string -> 'a) = <fun:clo@0_6> > invalid_arg;; val it : (string -> 'a) = <fun:clo@0_7> |
异常的捕捉采用如下的表达方法。
try-catch 表达式try
表达式
with
| 匹配模式1 -> 表达式2
| 匹配模式2 -> 表达式3
...
try-finally 表达式try 表达式1 finally 表达式2
在try-catch表达式中,如果在try表达式中引发异常,会依次将每个匹配模式与异常进行比较,并且,对于第一个匹配模式,将会执行该分支的对应表达式(称为“异常处理程序”),并且总体表达式将在该异常处理程序中返回该表达式的值。(在F#中称为try-with表达式)
第二段try-finally表达式为,无论表达式1中是否引发异常,都会执行表达式2中的代码,不捕捉异常。
另外,在其他语言中也有try-catch-finally这种三阶段的异常处理方法,但是在F#中并不支持该异常处理方法。(在OCaml中也不支持)。
应该注意的一点是,如果使用try-finally表达式,则必须在该表达式之外捕捉并处理表达式中可能引发的异常,否则程序终止。
接下来,我们来看实际的异常捕捉示例。
异常捕捉(try-catch表达式)
try //invalid_arg "不满10元"; failwith "不满10元" with | Failure "不满10元" -> printfn "余额小于10元" | Failure "不满100元" -> printfn "余额小于100元" | Failure "不满1000元" -> printfn "余额小于1000元" | Failure x -> printfn "您没有办理充值卡" | x -> rethrow();; |
另外,当不符合上面的几种情况时,为了抛出Invalid_arg异常时可以使用rethrow()函数重新抛出异常。rethrow函数是让捕捉到的异常重新抛出的函数。
下面是使用finally的示例。
异常捕捉(try-finally表达式)
try 1/0 finally printfn "请勿介意";; |
但是,因为finally后的代码会被执行,所以在运行结果中仍然会显示字符串。
上例中程序的执行结果
未被捕捉的异常: System.DivideByZeroException: 试图除以零。 位置 <StartupCode$zerodiv>.$Program._main() 位置 C:/Documents and Settings/username/My ocuments/Visual Studio 2008/Projects/zerodiv/Program.fs:行 4 请勿介意 |
基本上在如下场合中使用finally。
1.确保得到了内存等资源的释放。
2.即使引发了异常也要执行如下代码。
3.确保内存等资源的释放。
(语句2放在try表达式中,语句3放在finally表达式中)
如果不执行语句3,就会引起资源得不到释放或内存泄漏等情况.
try-finally示例2
open System.IO;; let fs = new FileStream("a.txt",FileMode.Create) in try fs.WriteByte(48uy); finally fs.Dispose();; |
但是,在这种情况下用use语句会使代码更加简洁明了。
与上述语句相同作用的代码
use fs = new FileStream("a.txt",FileMode.Create) in fs.WriteByte(48uy);; |
相关文章推荐
- F#入门-第二章 F#基础-第十八节 异常(二)
- F#入门-第二章 F#基础-第十二节 列表(list)
- F#入门-第二章 F#基础-第二十一节 类型推理
- F#入门-第二章 F#基础-第四节 let语句-值的绑定
- F#入门-第二章 F#基础-第八节 for循环
- F#入门-第二章 F#基础-第十三节 元组
- F#入门-第二章 F#基础-第二十二节 模块与命名空间
- F#入门-第二章 F#基础-第九节 while循环
- F#入门-第二章 F#基础-第一节 基本数据类型
- F#入门-第二章 F#基础-第十四节 数组
- F#入门-第二章 F#基础-第十九节 模式匹配(一)
- F#入门-第二章 F#基础-第十五节 记录
- F#入门-第二章 F#基础-第十节 引用类型
- F#入门-第二章 F#基础-第十六节 函数的类型
- 零基础入门深度学习 | 第二章:线性单元和梯度下降
- F#入门-第二章 F#基础-第五节 let语句-函数定义
- F#入门-第二章 F#基础-第十一节 mutable关键字
- F#入门-第二章 F#基础-第十九节 option类型
- C#入门基础(第二章)
- java 从零开始,学习笔记之基础入门<异常>(十五)