例外處理與 物件序列化(Exception Handling and Serialization of Objects) 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
錯誤代碼應用例虛擬碼(1/3) readFile { openFile; determineSize; allocateMemory; readFileIntoMemory; closeFile; } *吳瑞千譯, W. D. Mitchell原著, JAVA 程式疑難排解, 麥格羅希爾, 2001, pp. 3-8 ~3-10.
錯誤代碼應用例虛擬碼(2/3) errorCodeType readFile { initialize errorCode = 0; openFile; if( fileIsOpen ) { determineSize; if( gotFileLength ) { allocateEnoughMemory; if( memoryAllocated ) { readFileIntoMemory; if( readFailed ) { errorCode = -1; handleError; } else { errorCode = -2; handleError; }
錯誤代碼應用例虛擬碼(3/3) } else { errorCode = -3; handleError; } closeFile; if( fileDidNotClose && errorCode == 0 ) { errorCode = -4; handleError; errorCode = -5; handleError; return errorCode;
Try-Catch 應用例虛擬碼(1/2) readFile { try { openFile; determineSize; allocateMemory; readFileIntoMemory; closeFile;
Try-Catch 應用例虛擬碼(2/2) } catch( fileDidNotOpen ) { handleError; // or throwException } catch( sizeNotDetermined ) { } catch( memoryAllocationFailed ) { } catch( couldNotReadFile ) { } catch( couldNotCloseFile ) { }
例外處理優點 分離正常與錯誤處理程式碼,易於了解及偵錯 例外物件包含所需清楚資訊,減輕記憶負擔,易於使用 易於繼承衍生例外物件, 減少所需重複程式碼
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
UsingTryCatch.Program (1/3) /* * 示範try...catch之使用 * 5/14/2007 */ using System; namespace UsingTryCatch { class Program static void Main(string[] args)
UsingTryCatch.Program (2/3) { Console.Write("輸入除數: "); int number = int.Parse(Console.ReadLine()); int result = 5 / number; } catch (Exception e) Console.WriteLine("發生例外"); Console.WriteLine("例外原因: " + "\n\n" + e.Message + "\n"); Console.WriteLine("擲出例外之方法的呼叫堆疊: " + "\n\n" + e.StackTrace + "\n");
UsingTryCatch.Program (3/3) Console.WriteLine("擲出例外之地點: " + "\n\n" + e.TargetSite + "\n"); Console.WriteLine( "擲出例外之程式物件來源: " + "\n\n" + e.Source + "\n"); Console.WriteLine("例外之相關細節: " + "\n\n" + e.ToString() + "\n"); }
UsingTryCatch2.Program (1/3) /* 捕捉不同例外 * 5/14/2007 */ using System; namespace UsingTryCatch2 { class Program static void Main(string[] args) bool success = false; do try
UsingTryCatch2.Program (2/3) Console.Write("輸入除數: "); int number = int.Parse(Console.ReadLine()); int result = 5 / number; Console.WriteLine("除結果為" + result); success = true; } catch (FormatException e) { Console.WriteLine("請輸入數值"); Console.WriteLine(e.Message); catch (DivideByZeroException e) Console.WriteLine("不可除以零");
UsingTryCatch2.Program (3/3) catch (Exception e) { Console.WriteLine("其他錯誤"); Console.WriteLine(e.Message); } finally if (!success) Console.WriteLine("再試一次"); } while (!success);
練習 參考FileDemo程式,寫一程式打開一不存在之資料檔,觀察會有何例外發生 修改程式catch此例外,印出訊息後結束程式執行
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
UserDefinedException.Program (1/5) /* * 示範使用者自訂例外之使用 * 4/20/2008 */ using System; namespace UserDefinedException { class Program static void Main(string[] args) int nStudents = 3;
UserDefinedException.Program (2/5) Student[] table = new Student[nStudents]; try { table[0] = new Student("B645330", 90); Console.WriteLine("table[0] = {0} : {1}", table[0].RegNo, table[0].Grade); table[1] = new Student("B645331", 102); //table[1] = new Student("B645331", 100); Console.WriteLine("table[1] = {0} : {1}", table[1].RegNo, table[1].Grade); table[2] = new Student("B645332", 55); Console.WriteLine("table[2] = {0} : {1}", table[2].RegNo, table[2].Grade); //Console.WriteLine("table[3] = {0} : {1}", // table[3].RegNo, table[3].Grade); }
UserDefinedException.Program (3/5) catch (GradeOutOfRangeException e) { Console.WriteLine(e); } catch (IndexOutOfRangeException e) Console.WriteLine(e.Message); catch (Exception e)
UserDefinedException.Program (4/5) class Student { private string regNo; private int grade; public Student(string regNo, int grade) { if (grade < 0 || grade > 100) throw new GradeOutOfRangeException( regNo, grade); this.regNo = regNo; this.grade = grade; } public string RegNo { get { return regNo; } public int Grade { get { return grade; }
UserDefinedException.Program (5/5) public class GradeOutOfRangeException : ApplicationException { private string message; public GradeOutOfRangeException( string regNo, int grade) : base() message = "學生" + regNo + "之分數" + grade + "不在0與100之間"; } public override string ToString() return message;
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
例外類別階層 Exception ApplicationException SystemException ArithmeticException DivideByZeroException NotFiniteNumberException OverflowException FormatException InvalidCastException IndexOutOfRangeException NullReferenceException StackOverflowException IOException
練習 撰寫類別Square,模擬邊長為a的正方形。在以a為輸入參數之建構式中檢驗a是否為正,若否則throw一自訂之例外物件 撰寫此一自定之例外類別 撰寫測試程式,嘗試建立邊長為-1的正方形,並catch例外,輸出訊息後結束
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
NestedTryAndCatch.Program(1/7) /* * 示範巢狀try...catch敘述之使用 * 4/20/2008 */ using System; namespace NestedTryAndCatch { class Program static void Main(string[] args) int nStudents = 1; bool validNStudents = false;
NestedTryAndCatch.Program(2/7) do { Console.Write("輸入學生人數: "); try nStudents = int.Parse(Console.ReadLine()); if (nStudents > 0) validNStudents = true; } catch (FormatException e) Console.WriteLine(e.Message); catch (Exception e) } while (!validNStudents);
NestedTryAndCatch.Program(3/7) { Student[] table = new Student[nStudents]; for (int i = 0; i < nStudents; ++i) bool validGrades = false; do Console.WriteLine("輸入第" + (i + 1) + " 個學生學號及成績, 以一個空白分隔: "); string line; string[] data = new string[2]; line = Console.ReadLine(); data = line.Split(' ');
NestedTryAndCatch.Program(4/7) string regNo = data[0]; int grade = Convert.ToInt32(data[1]); table[i] = new Student(regNo, grade); Console.WriteLine( "table[" + i + "] = {0} : {1}", table[i].RegNo, table[i].Grade); validGrades = true; } catch (GradeOutOfRangeException e){ Console.WriteLine(e); catch (FormatException e) { Console.WriteLine(e.Message); } while (!validGrades);
NestedTryAndCatch.Program(5/7) catch (IndexOutOfRangeException e) { Console.WriteLine(e.Message); } catch (Exception e)
NestedTryAndCatch.Program(6/7) class Student { private string regNo; private int grade; public Student(string regNo, int grade) { if (grade < 0 || grade > 100) throw new GradeOutOfRangeException( regNo, grade); this.regNo = regNo; this.grade = grade; } public string RegNo { get { return regNo; } public int Grade { get { return grade; }
NestedTryAndCatch.Program(7/7) public class GradeOutOfRangeException : ApplicationException { private string message; public GradeOutOfRangeException(string regNo, int grade) : base() message = "學生" + regNo + "之分數" + grade + "不在0與100之間"; } public override string ToString() return message;
練習 將NestedTryAndCatch程式改為由資料檔讀取資料,注意catch檔案不存在的例外狀況 測試一錯誤資料檔,使程式提早遇到End Of File的情況,觀察有何例外發生 修改程式,使能處理End Of File的情況
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
串流(Stream) 檔案 程式變數 位元組串流 Test.dat 程式變數 位元組串流 網路接口 (Socket)
程式執行時自動產生的串流物件 類別Console的三個相關資料成員 Console.In Console.Out Console.Error 連接至鍵盤,標準輸入串流物件 用於Console函式Read與ReadLine Console.Out 連接至螢幕,標準輸出串流物件 用於Console函式Write與WriteLine Console.Error 連接至螢幕,標準錯誤訊息輸出串流物件
命名空間System.IO
System.IO中的類別 StreamReader StreamWriter FileStream MemoryStream 自檔案輸入文字 StreamWriter 輸出文字至檔案 FileStream 輸入及輸出至檔案均可能 MemoryStream BufferedStream
綱要 錯誤代碼 例外物件、try、catch、finally 自訂例外 例外類別的階層結構 巢狀try與catch 檔案類別 物件序列化
物件序列化(Serialization)與 去序列化(Deserialization) 利用BinaryFormatter,在Stream進行 序列化 將物件轉換為適當格式之資料,寫入檔案 去序列化 自檔案以適當格式讀出資料,重建物件
UsingSerialization.Program (1/8) /* * 以類別Student及GraduateStudent說明物件的序列化與 * 去序列化 * 11/2/2008 */ using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Diagnostics; namespace UsingSerialization { [Serializable] public class Student private string regNo; private string name;
UsingSerialization.Program (2/8) public Student() { regNo = ""; name = ""; } public Student(string regNo, string name){ this.regNo = regNo; this.name = name; public string RegNO { get { return regNo; } public string Name { get { return name; }
UsingSerialization.Program (3/8) [Serializable] public class GraduateStudent : Student { private string advisor; public GraduateStudent() : base() { advisor = ""; } public GraduateStudent( string regNo, string name, string advisor) : base(regNo, name) { this.advisor = advisor; public string Advisor { get { return advisor; }
UsingSerialization.Program (4/8) class Program { static void Main(string[] args) Student so1 = new Student( "B645331", "Thomas"); GraduateStudent so2 = new GraduateStudent( "F685329", "Richard", "Aaron"); BinaryFormatter formatter = new BinaryFormatter(); try { FileStream output = new FileStream("Test.dat", FileMode.Create, FileAccess.Write);
UsingSerialization.Program (5/8) formatter.Serialize(output, so1); formatter.Serialize(output, so2); output.Close(); } catch (SerializationException) { Console.WriteLine( "Error writting to file in output"); catch (IOException) "Can not create or close file for output"); try
UsingSerialization.Program (6/8) FileStream input = new FileStream("Test.dat", FileMode.Open, FileAccess.Read); try { while (true) Object obj = formatter.Deserialize(input); if ( obj.GetType() == so1.GetType()) Student si = (Student)obj;
UsingSerialization.Program (7/8) Console.WriteLine( si.RegNO + "\t" + si.Name ); } else if (obj.GetType() == so2.GetType()) { GraduateStudent si = (GraduateStudent)obj; Console.WriteLine( si.RegNO + "\t" + si.Name + "\t" + si.Advisor); else { throw new SerializationException();
UsingSerialization.Program (8/8) catch (SerializationException) { // close stream if no data left input.Close(); } catch (IOException) { Console.WriteLine( "Can not close file for input"); catch (FileNotFoundException) { "Can not open file in building intput");