什麼是參考型別
參考型別是將變數位址 (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
}
沒有留言:
張貼留言