Tài liệu Các Chủ Đề Tiến Bộ Trong C# part 1 - Pdf 10


Lỗi và xử lí biệt lệ
Không gì quan trọng bằng một đoạn mã tốt, chương trình của bạn phải luôn có khả năng
xử lí những lỗi có thể xảy ra.Ví dụ, giữa một quy trình xử lí phức tạp , đoạn mã của bạn
nhận ra rằng nó không được phép đọc một file, hoặc trong khi nó đang gửi yêu cầu đến
mạng thì mạng rớt.trong những tính huống ngoại lệ ( exception) nh
ư vậy, không có đủ
phương thức dù chỉ đơn giản là trả về một mã lỗi tương đương- có thể có khoảng 15 đến
20 lần gọi những phương thức lồng nhau,vì thế những gì bạn thật sự cần chương trình
làm là nhảy ngược trở lại xuyên suốt 15 đến 20 lần gọi để thoát nhiệm vụ một cách hoàn
chỉnh và xắp sếp lại những thứ bừ
a bộn. C# có những cách tốt để xử lí những loại tình
huống này,bằng cơ chế xử lí biệt lệ ( exception handling).
Cách thức xử lí lỗi trong VB rất hạn chế, bị giới hạn trong câu lệnh On Error Goto. Nếu
bạn đã học VB, bạn sẽ thấy những biệt lệ trong C# mở ra một thế giới mới cho việc xữ lí
lỗi trong chương trình của bạn. Mặt khác,những nhà phát triể
n Java và C++ sẽ quen với
những nguyên tắc biệt lệ bởi những ngôn ngữ này cũng xử lí lỗi theo cùng cách mà C# sử
dụng. Những nhà phát triển sử dụng C++ thỉnh thoảng cảnh giác với những biệt lệ bởi
việc thực thi ẩn trong C++ có thể xảy ra, nhưng điều này không cần quan tâm trong
C#.Sử dụng biệt lệ trong mã C# không gây bất kì ảnh hưởng bất lợi nào trong thực thi.
Những lớp biệ
t lệ của lớp cơ sở
Trong C#, một biệt lệ là một đối tượng được tạo ra ( hoặc được ném ) khi một trạng thái
lỗi biệt lệ cụ thể xuất hiện. những đối tượng này chứa đựng những thông tin mà giúp ích
cho việc truy ngược lại vấn đề. Mặc dù chúng ta có thể tự tạo ra những lớp biệt lệ riêng (
chúng ta sẽ làm sau này), .NET cũng cung cấp cho chúng ta nhiều lớp bi
ệt lệ được định
nghĩa trước.
Những lớp biệt lệ cơ bản
Trong phần này chúng ta sẽ xem xét một cách tổng quát một vài biệt lệ mà có giá trị

ệ nào bao phủ trạng thái lỗi
duy nhất đối với ứng dụng của bạn ,bạn nên dẫn xuất một cách trực tiếp hay gián tiếp từ
System.ApplicationException
Chúng ta sẽ không thảo luận về tất cả những lớp biệt lệ đuợc biểu diễn trong biểu đồ. bởi
vì , hầu hết các mục đích sử dụng đều thể hiện rỏ ràng qua tên của chúng.
Như đ
ã đề cập, StackOverflowException xuất hiện nếu một vùng bộ nhớ đươc dùng cho
Stack bị đầy.tràn Stack có thể xuất hiện, nếu một phương thức gọi đệ quy liên tiếp. Đây
là một lỗi nghiêm trọng.bởi vì nó ngăn ứng dụng bạn làm bất cứ điều gì ngoại trừ việc tắt
.
Một OverflowException là những gì sẽ xảy ra nếu bạn cố gắng ép một số int -40 vào
trong một số uint trong ngữ cảnh Checked.
Tuy nhiên trong trường hợp xử lí biệt lệ, những lí do thường dùng trong việc thêm vào
những lớp thừa kế đơn giản là dùng để chỉ rõ những trạng thái l
ỗi cụ thể hơn và thường
không cần để nạp chồng phương thức hoặc thêm bất kì cái mới nào ( mặc dù người ta
cũng thường thêm vào những thuộc tính mà mang những thông tin thêm về trạng thái
lỗi.) ví dụ bạn có thể có một lớp ArgumentException cơ sở chỉ định phương thức, gọi một
giá trị không tương thích, được truyền vào, và một lớp ArgumentNullException dẫn xuất
từ lớp này, mà được ch
ỉ định đặc biệt khi một đối số NULL được truyền vào.
Đón bắt biệt lệ
Cho rằng bạn có những đối tượng biệt lệ có giá trị , vậy làm thế nào chúng ta có thể sử
dụng chúng trong đoạn mã để bẫy những trạng thái lỗi? Để có thể giải quyết điều này
trong C# bạn thường là phải chia chương trình của bạn thành những khối thuộc 3 kiểu
khác nhau :
Khối try chứa đựng đoạn mã mà có dạng là một phần thao tác bình thường trong chương
trình của bạn, nhưng đoạn mã này có thể gặp phải một vài trạng thái lỗi nghiêm trọng.
Khối catch chứa đựng đoạn mã mà giải quyết những trạng thái lỗi nghiêm trọng trong
đoạn try

// dọn dẹp
}
thực sự , có một vài điều có thể thay đổi trong cú pháp trên.
- Ta có thể bỏ qua khối finally.
- Ta có thể cung cấp nhiếu khối catch mà ta muốn xử lí những kiểu lỗi khác nhau.
- Ta có thể bỏ qua khối catch, trong trường hợp cú pháp phục vụ không xác định biệt
lệ , nhưng phải đảm bảo rằng mã trong khối finally sẽ được thực thi khi việc thực thi rời
khỏi khối try .
Nhưng điều này đặ
t ra một câu hỏi : nếu đoạn mã đang chạy trong khối try, làm thế nào
nó biết chuyển đến khối catch nếu một lỗi xuất hiện? nếu một lỗi được thăm dò, mã sẽ
làm một việc gì đó được biết đến như là ném ra một biệt lệ , nói cách khác , nó chỉ ra một
lớp đối tượng biệt lệ và ném nó:
throw new OverflowException();
Ở đây chúng ta có một thể hiện của đối tượng biệt lệ của lớp OverflowException. ngay
khi máy tính gặp một câu lệnh throw bên trong khối try, nó ngay lập tức tìm khối catch
kết hợp với khối try, nó xác định khối catch đúng bởi việc kiểm tra lớp biệt lệ nào mà
khối catch kết hợp với. ví dụ, khi đối tượng OverflowException đuợc ném ra việc thực thi
sẽ nhảy vào khối catch sau:
catch (OverflowException e)
{
Nói cách khác, máy tính sẽ tìm khối catch mà chỉ định một thể hiện lớp biệt lệ phù hợp
trong cùng một một lớp ( hoặc của lớp cơ sở)
Giả sử, lúc xem xét đối số, có 2 lỗi nghiêm trọng có thể xảy ra trong nó: lỗi tràn và mảng
ngoài biên.giả sử rằng đoạn mã của chúng ta chứa đựng hai biến luậnlý. Overflow và
OutOfBounds , mà chỉ định liệu trạng thái lỗi này có tồn tại không .Chúng ta đã thấ
y lớp
biệt lệ đã định nghĩa trước đây tồn tại để chỉ định tràn (OverflowException);tương tự một
lớp IndexOutOfRangeException tồn tại để xử lí mảng ngoài biên.
Bây giờ hãy nhìn vào khối try sau:

nó lặp lại việc hỏi người sử dụng gõ
vào 1 số và trình bày nó tuy nhiên vì mục đích của ví dụ , chúng ta sẽ giả sử rằng số cần
gõ phải từ 0 > 5 hoặc là chương trình sẽ không thể xử lý số một cách chính xác. do đó
chúng ta sẽ ném ra một biệt lệ nếu người sử dụng gõ mộ thử gì đó ngoài vùng này.
Chương trình sẽ tiếp tục hỏi số cho đến khi người sử d
ụng nhấn enter mà không gõ bất kì
phím gì khác.
using System;

namespace Wrox.ProCSharp.AdvancedCSharp
{
public class MainEntryPoint
{
public static void Main()
{
string userInput;
while ( true )
{
try
{
Console.Write("Input a number between 0 and 5 " +
"(or just hit return to exit)> ");
userInput = Console.ReadLine();
if (userInput == "")
break;
int index = Convert.ToInt32(userInput);
if (index < 0 || index > 5)
throw new IndexOutOfRangeException(
"You typed in " + userInput);
Console.WriteLine("Your number was " + index);

System.Convert chứa đựng phương thức để thực thi nhiều kiểu chuyển đổi khác nhau ,
nhắc lại rằng trình biên dịch C# xem int là một thể hiện của lớp cơ sở System.int32 .
một điều cũng quan trọng nữa là thông số truyền đến khối catch chỉ có phạm vi trong
khối catch -chính vì vậy mà chúng ta mới có thể sử dụng cùng một tên thông số , e, trong
liên tiếp những khối block trong đ
oạn mã phía trên .
Trong đoạn mã trên chúng ta cũng kiểm tra chuỗi rỗng, bởi vì đây là điều kiện của chúng
ta để thoát khỏi vòng lặp while . chú ý cách câu lệnh break thực sự ngắt ra khỏi khối try
cũng như vòng lặp while. tất nhiên khi việc thực thi ngắt ra khỏi khối try, câu lệnh
Console.Writeline() trong khối finally được thực thi mặc dù chúng ta chỉ trình bày một
lời chaò ở đây, nhưng nhìn chung , chúng ta sẽ làm một số nhiệm vụ như là điều khiển
việc đóng file và gọi phương thức Dispose() của những đối tượng khác để thực hiện việc
dọn dẹp.mỗi lần máy tính rời khỏi khối finally, nó đơn giản chuyển việc thực thi đến câu
lệnh kế tiếp s
ẽ được thực thi, có khối finally không được tình baỳ.trong trường hợp này
chúng ta trở lại đầu vòng lặp while và bươc và khối try lần nữa.
Kế tiếp , chúng ta kiểm tra trạng thái biệt lệ:
if (index < 0 || index > 5)
throw new IndexOutOfRangeException("You typed in " + userInput);
Khi ném một biệt lệ, chúng ta cần chọn kiểu biệt lệ để ném.mặc dù lớp System.Exception
là có giá trị, nó thật sự được dùng như là một lớp cơ sở và là một cách lập trình không
hay khi ném một thể hiện c
ủa lớp này như là một biệt lệ, bởi vì nó không chuyển thông
tin gì về bản chất của trạng thái lỗi. thay vào đó microsoft định nghĩa nhiều lớp biệt lệ
khác mà dẫn xuất từ system.exception. mỗi lớp này phù hợp với một kiểu biệt lệ cụ thể
và bạn có quyền tự do định nghĩa một cái riêng của bạn.ý tưởng là bạn sẽ đưa ra càng
nhiều thông tin càng tốt về
trạng thái lỗi cụ thể bằng cách ném 1 thể hiện của lớp phù hợp
với trạng thái lỗi cụ thể.trong lúc naỳ, bạn chọn System.IndexOutOfRangeException như
là sự lưạ chọn tốt nhất trong tình huống naỳ. IndexOutOfRangeException có một vài hàm

catch thứ hai này lại nằm ở đây ? thật ra thì không chỉ đoạn mã của ta mới được bao phủ
bởi khối try ,mà bên trong khối, chúng ta thực sự gọi những phương thức riêng biệt nhau
trong namspace system ( Console.ReadLine(), Console.Write(), and Convert.ToInt32()),
và bất kỳ phương thức nào trong đ
ây cũng có thể ném ra một biệt lệ.
Nếu chúng ta gõ một thứ gì không phải là số - say hoặc hello ,sau đó phương thức
convert.toin32() sẽ ném ra một biệt lệ của lớp System.FormatException, để chỉ định
chuỗi được truyền vào toin32() không nằm trong định dạng mà có thể chuyển thành kiểu
int. khi điều này xảy ra, máy tính sẽ truy vết xuyên suốt phương thức gọi , tìm hàm xử lí
mà có thể xử lí biệt lệ này. khối catch đầ
u tiên của chúng ta ( cái mà bắt
IndexOutOfRangeException) sẽ không thực hiện.máy tính tìm đến cái thứ hai.cái này sẽ
thi hành bởi vì FormatException là một dẫn xuất từ exception, vì vậy một thực thể
FormatException có thể được truyền như là một thông số ở đây.
Cấu trúc của ví dụ trên thực sự là kiểu tình huống đẹp với nhiều khối catch.chúng ta sẽ
bắt đầu với khối catch được thiết kế để bẫy trạng thái lỗ
i cụ thể. sau đó, chúng ta hoàn
thành với nhiều khối catch sẽ bao phủ bất kì lỗi nào mà chúng ta không viết những hàm
xử lí lỗi cụ thể.việc sắp xếp các khối catch là quan trọng.nếu chúng ta viết tên 2 khối thứ
tự ngược nhau , mã sẽ không phiên dịch bởi vì khối catch thứ hai sẽ không bao giờ có thể
được tham chiếu đến ( khối catch exception có thể bắt tất cả các biệt lệ)
Tuy nhiên chúng ta cũng nhìn vào khối catch thứ ba:
catch
{
Console.WriteLine("Some other exception has occurred");
}
Đây là khối catch chung cho tất cả - nó không nhận bất kì thông số nào . lý do của khối
block này là bắt các biệt lệ được ném bởi những đoạn mã không được viết trong C#, hoặc
thậm chí không được quản lí trong C#. như bạn thấy, đó là một sự đòi hỏi của ngôn ngữ
C# mà chỉ thực thể của lớp được dẫn xuất từ system.exception mới có thể ném như một

những lớp cơ sở.ta thường gặp điều này.tuy nhiên các mã torng các thư viện hiếm khi bắt
các biệt lệ, xem đ
ó như là trách nhiệm của các mã client ( mã mà ta viết).
Thông thường, ta sẽ tìm các biệt lệ được ném từ các thư viện lớp cơ sở khi ta vá lỗi. mục
đích của ta là đảm bảo vào thời gian chạy của các chươn gtrình các biệt lệ thực sự chỉ
xuất hiện vào lúc nào đó và nếu có teh63 thì được xử lí theo một cách nào đó theo ý của
ta .

Những thuộctính của system.exception
Trong ví dụ trên chúng ta chỉ xem xét 1 thuộc tính đó là message, củ
a đối tượng
exception. tuy nhiên có 1 số những thuộc tính trong lớp system.exception
- helplink: 1 liên kết đến 1 tập tin trợ giúp mà cung cấp nhiều thông tin hơn về biệt lệ
- message: chuỗi mô tả trạng thái lỗi
- source : tên của ứng dụng hoặc đối tượng mà gây ra lỗi
- Stacktrace: chi tiết về phương thức gọi stack ( để giúp đỡ truy dấu vết phương thức
mà ném ra biệt lệ)
- TargetSite: một đối tượng phản ánh của .NET mà mô tả phươ
ng thức ném ra biệt lệ
- InnerException :nếu biệt lệ này được ném từ bên trong một khối catch , nó chứa đựng
đối tượng biệt lệ mà gửi đoạn mã vào trong khối catch.
Trong những thuộc tính này , StackTrace và TargetSite được cung cấp tự động bởi thời
gian chạy .NET nếu dấu vết Stack có giá trị. Source luôn luôn được điền bởi thời gian
chạy .NET như là tên của tập hợp, mà biệt lệ đuợc phát ra. Trong khi đó message
,helplink,innerexception phải được điền bởi mã mà ném ra biệt lệ, bằng việc thiết lập
những thuộc tính này ngay trước Khi ném ra biệt lệ,do đó trong ví dụ mã để ném biệt lệ
có thể là :
if (ErrorCondition == true)
{
Exception myException = new ClassmyException("Help!!!!");

// Point B
}
catch
{
// Point C
}
finally
{
// Dọn dẹp
}
// Point D
}
catch
{
// xử lí lỗi
}
finally
{
// dọn dẹp
}
Nếu biệt lệ được tung ra bên trong khối try ngoài nhưng bên ngoài khối try bên trong
(điểm a và d), thì tình huống sẽ giống như bạn đã được học trước đây.
Nếu biệt lệ đưọc ném bên trong khối try ( điểm B) có một khối catch thích hợp để xử lí
biệt lệ này, thì biệt lệ được xử lí ở đó và khối finally bên trong được thực thi trước khi
việc thực thi tiếp tục bên trong củ
a khối try nằm ngoài ( điểm D)
bây giờ giả sử một biệt lệ xuất hiện bên trong khối try trong và không có khối catch trong
để xử lí . lúc này khối finally trong được thực thi, nhưng sau đó thời gian chạy .NET sẽ
không có chọn lựa nào khác là rời khỏi toàn bộ khối try này và tìm kiếm hàm xử lí biệt lệ
thích hợp ở nơi tiếp theo là khối catch ngoài. nếu tìm được thì xử lí và nhảy dến khối

MortimerColdCall sẽ chứa đựng 2 khối try lồng nhau, và cũng
minh họa thực tập việc định nghĩa lớp biệt lệ tuỳ biến của riêng ta.và tung một biệt lệ
khác từ bên trong một khối try
Trong ví dụ này ,ta sẽ làm quen với công ty điện thoại di động mortimer phones ( phụ lục
A).giả sử rằng công ty muốn có thêm khách hàng. đội kinh doanh của cty sắp đưa ra danh
sách của những người được mời trở thành khách hàng hoặc để dùng cho kinh doanh. để
làm điều này ta có một tậ
p tin lưu tên của những người được gọi.tập tin nên được định
dạng chuẩn mà dòng đầu tiên chứa số người trong tập tin,và mỗi dòng tiếp theo chứa
đựng tên của người kế tiếp. ví dụ :
4
Avon from 'Blake's 7'
Zbigniew Harlequin
Simon Robinson
Christian Peak
Chương trình của ta sẽ hỏi người sử dụng tên tập tin : đơn giản đọc nó và trình bày tên
trong tập tin.
nghe có vẻ đơn giản nhưng có một vài lỗi có thể xảy ra:
Tên tập tin không tồ
n tại .biệt lệ FileNotFound sẽ tung ra
tập tin có thể định dạng không đúng.có 2 vấn đề có thể có : thứ nhất: dòng đầu tiên không
phải là một số nguyên. Thứ hai :số tên không khớp với số nguyên ở dòng đầu.để giải
quyết vấn đề này chúng ta phải tự xây dựng lấy lớp biệt lệ , ở đây ta đặt tên là
ColdCallFileFormatException
Cũng có những vấn đề khác tuy nhiên không nghiêm trọng , chúng ta có thể sử dụng các
khối try bên trong .ví dụ nếu là người gián điệp của công ty khác ( chử cái đầu là Z) thì
chúng ta có thể bỏ qua người đó và xử lí ngưòi kế tiếp. biệt lệ này được gọi là
LandLineSpyFoundException , tất nhiên đây là một đối tuợng biệt lệ tuỳ biến khác.
cuối cùng chúng ta sẽ thực thi ví dụ này bằng việc soạn 1 lớp,ColdCallFileReader, mà
duy trì việc nối giữa tập tin goị-lạnh ( cold-call ) và xuất dữ liệu từ nó.chúng ta so

Console.WriteLine("All callees processed correctly");
}
catch(FileNotFoundException e)
{
Console.WriteLine("The file {0} does not exist", fileName);
}
catch(ColdCallFileFormatException e)
{
Console.WriteLine(
"The file {0} appears to have been corrupted", fileName);
Console.WriteLine("Details of problem are: {0}", e.Message);
if (e.InnerException != null)
Console.WriteLine(
"Inner exception was: {0}", e.InnerException.Message);
}
catch(Exception e)
{
Console.WriteLine("Exception occurred:\n" + e.Message);
}
finally
{
peopleToRing.Dispose();
}
Console.ReadLine();
}
}

Trong khối try ta mở file ( phương thức ColdCallFileReader.Open() ) và lặp tất cả tên
trong đó.phương thức ColdCallFileReader.ProcessNextPerson() đọc và trình bày tên của
người kế tiếp trong tập tin.trong khi thuộc tính ColdCallFileReader.NPeopleToRing bảo

được xử lí bằng phưong thức open()
public void Open(string fileName)
{
if (isDisposed)
throw new ObjectDisposedException("peopleToRing");
fs = new FileStream(fileName, FileMode.Open);
sr = new StreamReader(fs);
try
{
string firstLine = sr.ReadLine();
nPeopleToRing = uint.Parse(firstLine);
isOpen = true;
}
catch (FormatException e)
{
throw new ColdCallFileFormatException(
"First line isn\'t an integer", e);
}
}
Dòng đầu tiên kiểm tra xem liệu mã client có gọi không hợp lí nó sau khi đối tượng bị
huỷ hay không .ném ra một đối tượng ObjectDisposedException được định nghĩa trước
nếu nó xuất hiện.
Kế tiếp phương thức chứa đựng khối try đầu tiên, mục đích của khối này là đón bắt bất kì
lỗi trả về từ dòng đầu tiên không chứa đựng một số nguyên, nếu vấn đề phát ra , thời gian
ch
ạy .NET sẽ tung ra biệt lệ FormatException mà chúng ta sẽ bắt và chuyển thành một
biệt lệ có nhiều ý nghĩa hơn.biệt lệ mới được tung ra sẽ bị bẫy bởi khối try ngoài nhất.
Nếu mọi thứ tốt đẹp , chúng ta sẽ thiết lập trường isopen là true để chỉ định việc kết nối
đến tập tin đang có giá trị.và dữ liệu có thể được đọc.
phương thức ProcessNextPerson() ch

Có 2 vấn đề cần quan tâm trong công đoạn này :
Thứ nhất là đọc tên của người gián điệp khi đó khối catch trong phương thức này sẽ xử lí.
bởi vì biệt lệ bị bắt ở đây bên trong vòng lặp nghĩa là việc thực thi có thể tiếp tục trong
phương thức main() của chương trình, và tên tiếp theo trong tập tin sẽ xử lí.
thứ hai là khi đọc tên của người kế tiếp thì thấy rằ
ng đã đến vị trí kết thúc tập tin , cách
mà phương thức StreamReader's ReadLine() làm việc là nếu chúng ta đã qua điểm kết
thúc tập tin , thì nó không ném ra biệt lệ mà chỉ trả về null, nếu ta tìm thấy chuỗi null ta
biết rằng tập tin định dạng sai và ta ném ra biệt lệ ColdCallFileFormatException mà sẽ
đưọc bắt bởi hàm xử lí biệt lệ bên ngoài.( là nguyên nhân của việc thực thi bị ngắt)
Thuộc tính NPeopleToRing cho biết số người dự định s
ẽ có trong tập tin.
Phương thức Dispose() mà đóng một tập tin . nó sẽ kiểm tra có thực là có một tập tin để
đóng trước khi đóng.
public uint NPeopleToRing
{
get
{
if (isDisposed)
throw new ObjectDisposedException("peopleToRing");
if (!isOpen)
throw new UnexpectedException(
"Attempt to access cold call file that is not open");
return nPeopleToRing;
}
}

public void Dispose()
{
if (isDisposed)

Chúng ta giả sử rằng thông điệp được truyền vào hàm dựng chỉ là tên của người gián điệp
được tìm thấy.chúng ta cũng cung cấp 2 hàm dựng , một cái đơn giản lấy thông điệp, cái
kia cũng lấy biệt lệ bên trong như là 1 thông số.khi định nghiã lớp biệt lệ riêng biệt ta nên
xây dựng ít nhất 2 hàm dựng ( mặc dù trong thí dụ trên chúng ta không cần hàm dựng thứ
2 )
Sau đây là lớp ColdCallFileFormatException. :
class ColdCallFileFormatException : ApplicationException
{
public ColdCallFileFormatException(string message)
: base(message)
{
}

public ColdCallFileFormatException(
string message, Exception innerException)
: base(message, innerException)
{
}
}

Và cuối cùng là lớp UnexpectedException mà cũng gần giống như lớp
ColdCallFieFormatException:
class UnexpectedException : ApplicationException
{
public UnexpectedException(string message)
: base(message)
{
}

public UnexpectedException(string message, Exception innerException)

Christian Peak
The file people2.txt appears to have been corrupted
Details of the problem are: Not enough names MortimerColdCall
Please type in the name of the file containing the names of the people to be cold-called >
people3.txt
The file people3.txt does not exist

Code for Download :
SimpleException

MortimerColdCall


Nhờ tải bản gốc

Tài liệu, ebook tham khảo khác

Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status