Tải bản đầy đủ (.pdf) (10 trang)

Giáo trình hình thành công thức điều chỉnh testfunc khi thực hiện chia với zero p1 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.38 MB, 10 trang )

Ngôn Ngữ Lập Trình C#
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// ta thử chia hai phần xử lý ngoại lệ riêng
public void TestFunc()
{
try
{
double a = 5;
double b = 0;
Console.WriteLine(“{0} / {1} = {2}”, a, b, DoDivide(a,b));
}
catch (System.DivideByZeroException)
{
Console.WriteLine(“DivideByZeroException caught!”);
}
catch (System.ArithmeticException)
{
Console.WriteLine(“ArithmeticException caught!”);
}
catch
{
Console.WriteLine(“Unknown exception caught”);
}
}
// thực hiện phép chia hợp lệ
public double DoDivide(double a, double b)


{
if ( b == 0)
throw new System.DivideByZeroException();
if ( a == 0)
throw new System.ArithmeticException();
return a/b;
}
}
Xử Lý Ngoại Lệ
372
Giáo trình hình thành công thức điều chỉnh
testfunc khi thực hiện chia với zero
.
Ngôn Ngữ Lập Trình C#
}

 Kết quả:
DivideByZeroException caught!

Trong ví dụ này, phương thức DoDivide() sẽ không cho phép chúng ta chia cho zero bởi một
số khác, và cũng không cho phép chia số zero. Nó sẽ phát sinh một đối tượng của Divide-
ByzeroException nếu chúng ta thực hiện chia với zero. Trong toán học việc lấy zero chia cho
một số khác là được phép, nhưng trong ví dụ minh họa của chúng ta không cho phép thực
hiện việc này, nếu thực hiện sẽ phát sinh ra một ngoại lệ ArithmeticException.
Khi một ngoại lệ được phát sinh, CLR sẽ kiểm tra mỗi khối xử lý ngoại lệ theo thứ tự và sẽ
lấy khối đầu tiên thích hợp. Khi chúng ta thực hiện với a=5 và b=7 thì kết quả như sau:
5 / 7 = 0.7142857142857143
Như chúng ta mong muốn, không có ngoại lệ được phát sinh. Tuy nhiên, khi chúng ta thay
đổi giá trị của a là 0, thì kết quả là:
ArithmeticException caught!

Ngoại lệ được phát sinh, và CLR sẽ kiểm tra ngoại lệ đầu tiên: DivideByZeroException. Bởi
vì không phù hợp, nên nó sẽ tiếp tục đi tìm và khối xử lý ArithmeticException được chọn.
Cuối cùng, giả sử chúng ta thay đổi giá trị của b là 0. Khi thực hiện điều này sẽ dẫn đến ngoại
lệ DivideByZeroException.
Ghi chú: Chúng ta phải cẩn thận thứ tự của câu lệnh catch, bởi vì DivideByZero-
Exception được dẫn xuất từ ArithmeticException. Nếu chúng ta đảo thứ tự của câu lệnh
catch, thì ngoại lệ DivideByZeroException sẽ được phù hợp với khối xử lý ngoại lệ Arith-
meticException. Và việc xử lý ngoại lệ sẽ không bao giờ được giao cho khối xử lý
DivideByZeroException. Thật vậy, nếu thứ tự này được đảo, nó sẽ không cho phép bất cứ
ngoại lệ nào được xử lý bởi khối xử lý ngoại lệ DivideByZeroException. Trình biên dịch sẽ
nhận ra rằng DivideByZeroException không được thực hiện bất cứ khi nào và nó sẽ thông
báo một lỗi biên dịch.
Chúng ta có thể phân phối câu lệnh try/ catch, bằng cách bắt giữ những ngoại lệ xác định
trong một hàm và nhiều ngoại lệ tổng quát trong nhiều hàm. Mục đích của thực hiện này là
đưa ra các thiết kế đúng. Giả sử chúng ta có phương thức A, phương thức này gọi một phương
thức khác tên là phương thức B, đến lượt mình phương thức B gọi phương thức C. Và phương
thức C tiếp tục gọi phương thức D, cuối cùng phương thức D gọi phương thức E. Phương thức
E ở mức độ sâu nhất trong chương trình của chúng ta, phương thức A, B ở mức độ cao hơn.
Nếu chúng ta đoán trước phương thức E có thể phát sinh ra ngoại lệ, chúng ta có thể tạo ra
khối try/catch để bắt giữ những ngoại lệ này ở chỗ gần nơi phát sinh ra ngoại lệ nhất. Chúng
ta cũng có thể tạo ra nhiều khối xử lý ngoại lệ chung ở trong đoạn chương trình ở mức cao
trong trường hợp những ngoại lệ không đoán trước được.
Xử Lý Ngoại Lệ
373
.
.
Ngôn Ngữ Lập Trình C#
Câu lệnh finally
Trong một số tình huống, việc phát sinh ngoại lệ và unwind stack có thể tạo ra một số vấn
đề. Ví dụ như nếu chúng ta mở một tập tin hay trường hợp khác là xác nhận một tài nguyên,

chúng ta có thể cần thiết một cơ hội để đóng một tập tin hay là giải phóng bộ nhớ đệm mà
chương trình đã chiếm giữ trước đó.
Ghi chú: Trong ngôn ngữ C#, vấn đề này ít xảy ra hơn do cơ chế thu dọn tự động của C#
ngăn ngừa những ngoại lệ phát sinh từ việc thiếu bộ nhớ.
Tuy nhiên, có một số hành động mà chúng ta cần phải quan tâm bất cứ khi nào một ngoại
lệ được phát sinh ra, như việc đóng một tập tin, chúng ta có hai chiến lược để lựa chọn thực
hiện. Một hướng tiếp cận là đưa hành động nguy hiểm vào trong khối try và sau đó thực hiện
việc đóng tập tin trong cả hai khối catch và try. Tuy nhiên, điều này gây ra đoạn chương
trình không được đẹp do sử dụng trùng lắp lệnh. Ngôn ngữ C# cung cấp một sự thay thế tốt
hơn trong khối finally.
Đoạn chương trình bên trong khối catch được đảm bảo thực thi mà không quan tâm đến
việc khi nào thì một ngoại lệ được phát sinh. Phương thức TestFunc() trong ví dụ 13.5 minh
họa việc mở một tập tin như là hành động đầu tiên của nó, sau đó phương thức thực hiện một
vài các phép toán toán học, và sau đó là tập tin được đóng. Có thể trong quá trình mở tập tin
cho đến khi đóng tập tin chương trình phát sinh ra một ngoại lệ. Nếu xuất hiện ngoại lệ, và
khi đó tập tin vẫn còn mở. Người phát triển biết rằng không có chuyện gì xảy ra, và cuối của
phương thức này thì tập tin sẽ được đóng. Do chức năng đóng tập tin được di chuyển vào
trong khối finally, ở đây nó sẽ được thực thi mà không cần quan tâm đến việc có phát sinh
hay không một ngoại lệ trong chương trình.
 Ví dụ 13.5: Sử dụng khối finally.

namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();

}
// chia hai số và xử lý ngoại lệ nếu có
public void TestFunc()
{
try
Xử Lý Ngoại Lệ
374
.
.
Ngôn Ngữ Lập Trình C#
{
Console.WriteLine(“Open file here”);
double a = 5;
double b = 0;
Console.WriteLine(“{0} /{1} = {2}”, a, b, DoDivide(a,b));
Console.WriteLine(“This line may or not print”);
}
catch (System.DivideByZeroException)
{
Console.WriteLine(“DivideByZeroException caught!”);
}
catch
{
Console.WriteLine(“Unknown exception caught”);
}
finally
{
Console.WriteLine(“Close file here.”);
}
}

// thực hiện chia nếu hợp lệ
public double DoDivide(double a, double b)
{
if ( b == 0)
{
throw new System.DivideByZeroException();
}
if ( a == 0)
{
throw new System.ArithmeticException();
}
return a/b;
}
}
}

 Kết quả:
Open file here
Xử Lý Ngoại Lệ
375
.
.
Ngôn Ngữ Lập Trình C#
DivideByZeroException caught!
Close file here.
Kết quả trong trường hợp b = 12
Open file here
5/ 12 = 0.416666666666
Close file here


Trong ví dụ này một khối catch được loại bỏ và thêm vào khối finally. Bất cứ khi một ngoại
lệ có được phát sinh ra hay không thì khối lệnh bên trong finally cũng được thực thi. Do vậy
nên trong cả hai trường hợp ta cũng thấy xuất hiện thông điệp “Close file here”.
Những đối tượng ngoại lệ
Cho đến lúc này thì chúng ta có thể sử dụng tốt các ngoại lệ cũng như cách xử lý khắc
phục các ngoại lệ này. Trong phần này chúng ta sẽ tiến hành việc tìm hiểu các đối tượng được
xây dựng cho việc xử lý ngoại lệ. Đối tượng System.Exception cung cấp một số các phương
thức và thuộc tính hữu dụng. Thuộc tính Message cung cấp thông tin về ngoại lệ, như là lý do
tại sao ngoại lệ được phát sinh. Thuộc tính Message là thuộc tính chỉ đọc, đoạn chương trình
phát sinh ngoại lệ có thể thiết lập thuộc tính Message như là một đối mục cho bộ khởi dựng
của ngoại lệ. Thuộc tính HelpLink cung cấp một liên kết để trợ giúp cho các tập tin liên quan
đến các ngoại lệ. Đây là thuộc tính chỉ đọc. Thuộc tính StackTrace cũng là thuộc tính chỉ đọc
và được thiết lập bởi CLR. Trong ví dụ 13.6 thuộc tính Exception.HelpLink được thiết lập và
truy cập để cung cấp thông tin cho người sử dụng về ngoại lệ DivideBy-ZeroException.
Thuộc tính StackTrace của ngoại lệ được sử dụng để cung cấp thông tin stack cho câu lệnh
lỗi. Một thông tin stack cung cấp hàng loạt các cuộc gọi stack của phương thức gọi mà dẫn
đến những ngoại lệ được phát sinh.
 Ví dụ 13.6: Làm việc với đối tượng ngoại lệ.

namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// chia hai số và xử lý ngoại lệ

Xử Lý Ngoại Lệ
376
.
.
Ngôn Ngữ Lập Trình C#
public void TestFunc()
{
try
{
Console.WriteLine(“Open file here”);
double a = 12;
double b = 0;
Console.WriteLine(“{0} /{1} = {2}”, a, b, DoDivide(a,b));
Console.WriteLine(“This line may or not print”);
}
catch (System.DivideByZeroException e)
{
Console.WriteLine(“\nDivideByZeroException! Msg: {0}”, e.Message);
Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink);
Console.WriteLine(“\nHere’s a stack trace: {0}\n”, e.StackTrace);
}
catch
{
Console.WriteLine(“Unknown exception caught”);
}
}
// thực hiện phép chia hợp lệ
public double DoDivide( double a, double b)
{
if ( b == 0)

{
DivideByZeroException e = new DivideByZeroException();
e.HelpLink = “”;
throw e;
}
if ( a == 0)
{
throw new ArithmeticException();
}
return a/b;
}
}
}
Xử Lý Ngoại Lệ
377
.
.
Ngôn Ngữ Lập Trình C#

 Kết quả:
Open file here
DivideByZeroExceptión Msg: Attempted to divide by zero
HelpLink:
Here’s a stack trace:
at Programming_CSharp.Test.DoDivide(Double c, Double b)
in c:\ exception06.cs: line 56
at Programming_CSharp.Test.TestFunc() in exception06.cs: line 22.
Close file here

Trong đoạn kết quả trên, danh sách trace của stack được hiển thị theo thứ tự ngược lại thứ tự

gọi. Nó hiển thị một lỗi trong phương thức DoDivde(), phương thức này được gọi từ phương
thức TestFunc(). Khi các phương thức gọi lồng nhau nhiều cấp, thông tin stack có thể giúp
chúng ta hiểu thứ tự của các phương thức được gọi.
Trong ví dụ này, hơn là việc đơn giản phát sinh một DidiveByZeroException, chúng ta tạo
một thể hện mới của ngoại lệ:
DivideByZeroException e = new DivideByZeroException();
Chúng ta không truyền vào thông điệp của chúng ta, nên thông điệp mặc định sẽ được in ra:
DivideByZeroException! Msg: Attemped to divide by zero.
Ở đây chúng ta có thể bổ sung như dòng lệnh bên dưới để truyền vào thông điệp của chúng ta
tùy chọn như sau:
new DivideByZeroException(“You tried to divide by zero which is not meaningful”);
Trước khi phát sinh ra ngoại lệ, chúng ta thiết lập thuộc tính HelpLink như sau:
e.HelpLink = “”;
Khi ngoại lệ được bắt giữ, chương trình sẽ in thông điệp và HelpLink ra màn hình:
catch (System.DivideByZeroException e)
{
Console.WriteLine(“\nDivideByZeroException! Msg: {0}”, e.Message);
Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink);
}
Việc làm này cho phép chúng ta cung cấp những thông tin hữu ích cho người sử dụng. Thêm
vào đó thông tin stack cũng được đưa ra bằng cách sử dụng thuộc tính StackTrace của đối
tượng ngoại lệ:
Console.WriteLine(“\n Here’s a stack trace: {0}\n”, e.StackTrace);
Kết quả là các vết trong stack sẽ được xuất ra:
Here’s a stack trace:
at Programming_CSharp.Test.DoDivide(Double c, Double b)
Xử Lý Ngoại Lệ
378
.
.

Ngôn Ngữ Lập Trình C#
in c:\ exception06.cs: line 56
at Programming_CSharp.Test.TestFunc() in exception06.cs: line 22.
Lưu ý rằng, phần đường dẫn được viết tắt, do đó kết quả của bạn có thể hơi khác một tí.
Bảng 13.1 sau mô tả một số các lớp ngoại lệ chung được khai báo bên trong namespace
System.
CÁC LỚP NGOẠI LỆ
Tên ngoại lệ Mô tả
MethodAccessException Lỗi truy cập, do truy cập đến thành viên hay
phương thức không được truy cập
ArgumentException Lỗi tham số đối mục
ArgumentNullException Đối mục Null, phương thức được truyền đối mục
null không được chấp nhận
ArithmeticException Lỗi liên quan đến các phép toán
ArrayTypeMismatchException Kiểu mảng không hợp, khi cố lưu trữ kiểu không
thích hợp vào mảng
DivideByZeroException Lỗi chia zero
FormatException Định dạng không chính xác một đối mục nào đó
IndexOutOfRangeException Chỉ số truy cập mảng không hợp lệ, dùng nhỏ hơn
chỉ số nhỏ nhất hay lớn hơn chỉ số lớn nhất của
mảng
InvalidCastException Phép gán không hợp lệ
MulticastNotSupportedException Multicast không được hỗ trợ, do việc kết hợp hai
delegate không đúng
NotFiniteNumberException Không phải số hữu hạn, số không hợp lệ
NotSupportedException Phương thức không hỗ trợ, khi gọi một phương
thức không tồn tại bên trong lớp.
NullReferenceException Tham chiếu null không hợp lệ.
OutOfMemoryException Out of memory
OverflowException Lỗi tràn phép toán

StackOverflowException Tràn stack
TypeInitializationException Kiểu khởi tạo sai, khi bộ khởi dựng tĩnh có lỗi.
Bảng 13.1 : Các ngoại lệ thường xuất hiện.
Tạo riêng các ngoại lệ
CLR cung cấp những kiểu dữ liệu ngoại lệ cơ bản, trong ví dụ trước chúng ta đã tạo một
vài các kiểu ngoại lệ riêng. Thông thường chúng ta cần thiết phải cung cấp các thông tin mở
rộng cho khối catch khi một ngoại lệ được phát sinh. Tuy nhiên, có những lúc chúng ta
Xử Lý Ngoại Lệ
379
.
.
Ngôn Ngữ Lập Trình C#
muốn cung cấp nhiều thông tin mở rộng hay là các khả năng đặc biệt cần thiết trong ngoại lệ
mà chúng ta tạo ra. Chúng ta dễ dàng tạo ra các ngoại lệ riêng, hay còn gọi là các ngoại lệ tùy
chọn (custom exception), điều bắt buộc với các ngoại lệ này là chúng phải được dẫn xuất từ
System.ApplicationException. Ví dụ 13.7 sau minh họa việc tạo một ngoại lệ riêng.
 Ví dụ: Tạo một ngoại lệ riêng.

namespace Programming_CSharp
{
using System;
// tạo ngoại lệ riêng
public class MyCustomException : System.ApplicationException
{
public MyCustomException( string message): base(message)
{
}
}
public class Test
{

public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// chia hai số và xử lý ngoại lệ
public void TestFunc()
{
try
{
Console.WriteLine(“Open file here”);
double a = 0;
double b = 5;
Console.WriteLine(“{0} /{1} = {2}”, a, b, DoDivide(a,b));
Console.WriteLine(“This line may or not print”);
}
catch (System.DivideByZeroException e)
{
Console.WriteLine(“\nDivideByZeroException! Msg: {0}”, e.Message);
Xử Lý Ngoại Lệ
380
.
.
Ngôn Ngữ Lập Trình C#
Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink);
}
catch (MyCustomException e)
{
Console.WriteLine(“\nMyCustomException! Msg: {0}”, e.Message);
Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink);

}
catch
{
Console.WriteLine(“Unknown excepiton caught”);
}
finally
{
Console.WriteLine(“Close file here.”);
}
}
// thực hiện phép chia hợp lệ
public double DoDivide( double a, double b)
{
if ( b == 0)
{
DivideByZeroException e = new DivideByZeroException();
e.HelpLink = “”;
throw e;
}
if ( a == 0)
{
MyCustomException e = new MyCustomException(“Can’t have zero
divisor”);
e.HelpLink = “”;
throw e;
}
return a/b;
}
}
}


Xử Lý Ngoại Lệ
381
.
.

×