什麼是參考型別
參考型別是將變數位址 (ponter) 存在 Stack 中,但該變數位圵則指到一塊稱為「堆積(Heap)」 的記憶體區塊,該區塊才是真正資料儲存的地方。
若某個變數是參考型別,當複製該變數時,實際上複製的是另外一份參考,而這個參考會指向 Heap 中同一個記憶體位置。底下範例可看出二者實際上的差異:
struct sNum { public int val; public sNum(int _val) { val = _val; } } class cNum { public int val; public cNum(int _val){val = _val;} } private void button2_Click(object sender, EventArgs e) { sNum s1 = new sNum(5); sNum s2 = s1; s2.val = 3; Console.WriteLine("s1={0} , s2={1}", s1.val, s2.val); //s1=5 , s2=3 cNum c1 = new cNum(5); cNum c2 = c1; c1.val = 3; Console.WriteLine("c1={0} , c2={1}", c1.val, c2.val); //c1=3 , c2=3 int i = 100; object obj = i; }
補充說明
堆疊 (Stack) 和堆積 (Heap) 是記憶體中,用來存放資料的兩種不同管理機制。
- Stack:當程式載入時,就配置好固定大小的記憶體空間;當變數生命周期結束時,就由記憶體中移除。
- Heap:當程式需要使用時,才透過記憶體配置方法,動態建立;當變數生命周期結束時,僅註明沒人使用,必須由程序釋放或由系統不定時的回收清空。
int i = 100; object obj = i;
變數 i 是實值型別,資料存在 stack 裡
變數 obj 是參考型別,資料存在 heap 裡
當執行 obj = i 時,系統會將 100 從 stack 複製一份到 heap 之中,然後 obj 就指向這個位址,這就稱為 Boxing。
反之,若將 heap 中的值,複製到stack之中,就稱為 Unboxing。
將數值資料轉換成物件稱為 Boxing ,把物件轉成數值稱為 Unboxing 。
內建參考型別
Object、String、Array、Stream、Exception等等都是內建參考型別 (Built-in Reference Types)
Strings and String Builders
string s; s = "wombat"; // "wombat" s += " kangaroo"; // "wombat kangaroo" s += " wallaby"; // "wombat kangaroo wallaby" s += " koala"; // "wombat kangaroo wallaby koala" Console.WriteLine(s); string[] ss = { "wombat", "kangaroo", "wallaby", "koala" }; string ss_join = string.Join(" ", ss); Console.WriteLine(ss_join); //wombat kangaroo wallaby koala string ss_concat = string.Concat(ss); Console.WriteLine(ss_concat); //wombatkangaroowallabykoala StringBuilder sb = new StringBuilder(); sb.Append("wombat"); sb.Append(" kangaroo"); sb.Append(" wallaby"); sb.Append(" koala"); Console.WriteLine(sb.ToString()); //wombat kangaroo wallaby koala
Create and Sort Arrays
string[] ss = { "wombat", "kangaroo", "wallaby", "koala" }; Array.Sort(ss); Console.WriteLine("{0}, {1}, {2}, {2}", ss[0], ss[1], ss[2], ss[3]); //kangaroo, koala, wallaby, wallaby
How to Use Streams
// Create and write to a text file StreamWriter sw = new StreamWriter("text.txt"); sw.WriteLine("Hello, World!"); sw.Close(); // Read and display a text file StreamReader sr = new StreamReader("text.txt"); Console.WriteLine(sr.ReadToEnd()); sr.Close();
How to Throw and Catch Exceptions
底下是一個Exception的程式碼片段,有幾點值得注意一下的:
- 1.Catch區塊最好以最特定到最不特定的方式排序 (most specific => least specific)
- 2.Finally區塊,不管有無例外,這個區塊都會執行,所以可在這個區塊清除不必要的物件。
StreamReader sr = null; try { sr = new StreamReader("test.txt"); Console.WriteLine(sr.ReadToEnd()); } catch (System.IO.FileNotFoundException ex) { Console.WriteLine(ex.Message); } catch (System.UnauthorizedAccessException ex) { Console.WriteLine(ex.Message); } catch (Exception ex) { Console.WriteLine(ex.Message); //找不到檔案 'D:\MCTS\MCTS2011\Practice\Practice\bin\Debug\test.txt'。 Console.WriteLine(ex.StackTrace); //於 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) //於 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath) //於 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) //於 System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize) //於 System.IO.StreamReader..ctor(String path) //於 Practice.Chapter1.Lesson2.button3_Click(Object sender, EventArgs e) 於 ....Lesson2.cs: 行 73 Console.WriteLine(ex.Source); //mscorlib } finally { if (sr!=null) sr.Close(); }
Object 型別
- 任何參考型別(字串、陣列、類別或介面)或實值型別(數字、Boolean、Char、Date、結構或列舉)都可以指派給 Object 變數。
- Object 的預設值為 Null。
- 你可以使用 GetTypeCode 方法取得目前 Object 變數所參考的資料型別。
- Object 資料型別就是參考型別。但是,如果它參考的是實值型別的資料時,就會被視為實值型別。
- 無論它所參考的資料型別為何,Object 變數都不會包含資料值本身,而是值的指標。 它在電腦記憶體中所佔的空間為 4 個位元組,但這並不包括儲存表示變數值的資料。
String 型別
字串物件是由 Char 物件所組成的一種物件,它是循序唯讀的集合。
string str = "hello"; // 字串變收是唯讀不可變動的 char tmp = str[2]; //str[2] = 'a'; <--這是不允許的
參數傳遞
下面這個例子,是使用委派,傳遞一個類別物件給委派指定的方法,看看參數值的變化。
testEmployee test; Employee emp1 = new Employee{ Name="vito", Age=30 }; test = delegate(Employee arg) { arg.Name = "shao"; }; test(emp1); Console.WriteLine(emp1.Name); //shao testEmployee test2; test2 = delegate(Employee arg) { Employee emp2 = new Employee { Name = "peter", Age = 40 }; arg = emp2; Console.WriteLine(arg.Name); }; test2(emp1); Console.WriteLine(emp1.Name); //shao
上面這個例子的重點在第14行, 當 emp1 被傳進方法中時,會複製一份指標位址給 arg,此時 arg 和 emp1 都指向相同的位址,但 arg 和 emp1 是不相同的。 arg 後來被改成指向 emp2,但當方法執行終了,生命週期也就跟著結束。
下面這個例子,比較 int, object, string, class object 等型別,在當做參數傳遞時的差異。
private void test1(int x) { x = 5; } private void test2(ref int x) { x = 5; } private void test3(object x) { x = 5; } private void test4(StringBuilder x) { x.Replace('3', '5'); } private void test5(Emp x) { x.salary = 5; } public class Emp { public Emp(int s) { salary = s; } public int salary { get; set; } } private void button9_Click(object sender, EventArgs e) { int a = 3; int b = 3; object c = 3; object d = "3"; StringBuilder sb = new StringBuilder("3"); Emp emp = new Emp(3); test1(a); test2(ref b); test3(c); test3(d); test4(sb); test5(emp); Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(c); Console.WriteLine(d); Console.WriteLine(sb); Console.WriteLine(emp.salary); //3 //5 //3 //3 //5 //5 }
沒有留言:
張貼留言