2013年9月3日 星期二

小山的 C# 教學-第25課-Private 的常見用途

本課簡介

上一課介紹了 public & private 這兩種關鍵字的使用方式
但是也許大家仍會對 private 的用途存疑

因此本課將介紹三種 private 常見的用途
讓大家了解 private 所帶來的好處以及安全性

教學影片

注意:影片有高畫質 720P 的選項,可以看得更清楚喔!



重點提示

private 在以下三種狀況能夠使用:

1. 有些 Property 想要隱藏起來,不想讓 class 外的東西能夠隨意存取

像是: Password 密碼
因此可以將 Password 直接設為 private
如果有比對密碼的需求,只需要簡單寫一個 public 的 method 來做比較的工作即可

2. 有些 Property 只想設成唯讀,就是只能看,不能夠修改

像是:Account 帳號
同樣先將 Account 設成 private
然後建立一個 public 的 method 叫做 getAcoount 來讓 class 外的東西取得 Account 的內容

3. 想要讓某些 Property 在設定上有所限制

像是:HP 血量,限制:HP 不可以為負數
首先先將 HP 設為 private
然後建立一個 public 的 method 像是本課所提的 hurt 來間接設定它
如果想要觀看 HP 的數值,只要像 (2) 一樣
建立 getHP 的 method 即可

補充

== & equals(string target)

在本課中,我們有比對兩個字串是否相等。相信有學過 Java 的人,應該會發現一件怪事。為什麼比對字串相等是使用「==」,而不是用「equal(string target)」?

事實上,使用 equals 也正確。但是在 C# 中,只要兩字串內的文字相同,就會參照到同個物件。因此如果使用「==」去比較記憶體位置,也會得到相同的值。這在 Java 裡面則會隨著建立 String 的方式不同而有所變化。

因此要記住,在 Java 內,比較字串相等一定要用「equals」;但在 C# 內,使用「==」也可以用做比較字串相等。

注意:以上畫線內容有誤,以下將作修正 (2013/10/24)

感謝網友「小明」提問,我才注意到這個錯誤

我原本是說「只要文字相同,就會參照到同一個記憶體位置」,這句話其實只對一半。事實上,C# 有維護一個稱為「String Pool」(字串池、字串倉庫) 的記憶體空間。C# 會在你的程式編譯的時候,就先把確定知道內容的字串變數都先檢查過,然後把這些字串的值都存在的 String Pool 裡面,最後讓值應該相同的變數指向同一個物件。

舉個例子來說,下面這段程式碼:
String a = "123"; String b = "123"; 我們不需要執行就知道 a 跟 b 的字串都是 "123",所以 C# 就會讓 a 跟 b 指向同一個 String 物件。

但是如果是下面這種狀況:
String a = "123"; String b = "1"; String c = "23"; // 中間可能有其他程式碼 String d = b + c; 那麼 C# 就無法馬上知道 d 會等於多少,因為第四行程式碼可能離前三行很遠,中間 b, c 遭到修改也沒人知道,所以就算最後 a, d 都是 "123",兩個也都會個別指向不同的物件

另外還有一種情況:
String a = "ccc"; String b = new String('c', 3); 上面這段程式碼,b 會得到 "ccc",也就是跟 a 一樣的字串。但是因為我們利用 new 這個指令,強迫 C# 一定要建立一個 String 物件,所以就算程式在編譯的時候就知道 a, b 相等,仍會讓他們指向不同物件

最後,以上所有結果,你們使用「==」去比較,還是會全部得到 ture!為什麼?因為 C# 特別處理了 String 比較的情況!一般來說,如果拿 Reference Type 的東西用「==」比較,確實會比較它們的記憶體位置是否相等。但是 C# 有特別規定,如果被比較的東西是 String 的話,那麼就要比較它們的存的「值」是否相等。不過,我個人建議大家,還是用 equals 來比較字串會安全許多。

另外,如果你們想要比較兩個字串的記憶體位置是否相同,那請使用:
String.ReferenceEquals(Object objA,Object objB); 詳細使用方式可以參考下面的連結。

相關資訊連結

網友 larry nung 對 String Pool 的介紹

http://www.dotblogs.com.tw/larrynung/archive/2011/06/30/30763.aspx

微軟官方(MSDN)對於「==」運算子的介紹 (裡面特別提到 String 比較的方式不同)

http://msdn.microsoft.com/zh-tw/library/53k8ybth%28v=vs.90%29.aspx

微軟官方(MSDN)對於 ReferenceEquals() 使用方法的介紹

http://msdn.microsoft.com/zh-tw/library/system.object.referenceequals%28v=vs.90%29.aspx

8 則留言:

  1. 小山老師想請問一下 有一些方法的問題
    通常執行方法會類似這樣

    方法名稱(變數A,變數B,變數C);

    但是有些指令 好像可以

    方法名稱(變數A,變數B,變數C(可有可無));

    這種的會變動變數數量的方法 該如何使用?

    謝謝~

    回覆刪除
    回覆
    1. 喔找到方法了
      好像是
      方法名稱(int A,string b = "AAAA")
      {}

      刪除
    2. 恩恩,那是一種預設初始值得方法
      其實是繼承 C/C++ 而來的

      刪除
  2. 您好,關於您補充內容我有些不明白。
    "在 C# 中,只要兩字串內的文字相同,就會參照到同個物件。因此如果使用「==」去比較記憶體位置,也會得到相同的值。"
    這句話的意思指的是什麼呢?特別是"兩字串內的文字相同,就會參照到同個物件。"這句特別不懂。
    是指說"只有在比較時"兩字串內文字如果相同,則程式會自動參照到同個物件,因此參照到的記憶體位置也相同,因而實際上使用"=="時會做記憶體位置的比較是嗎?

    況且您說在C#當中只要文字相同都會參照到相同物件,那不就表示宣告不同名稱的字串只要指定給他的文字一樣,兩者其實都參照到同一個物件,只共用一份記憶體空間,修改時不管改哪個名稱的字串都會修改到其內容嗎?

    回覆刪除
  3. 基本上就是閣下所想的意思

    假設我今天宣告了一個字串變數 A = "123";
    然後又宣告了另一個字串變數 B = "123";
    而在 C# 中,A 跟 B 會指向同一個「字串物件」
    這就是為何記憶體位置會相同

    如果你還記得之前我之前提過的
    變數有分成 Value 與 Reference Type
    那麼 == 在比較 Value Type 時,確實是比較實際存的內容
    而比較 Reference Type,則會像上面所說比較「兩者的記憶體位置」

    因此 String 是屬於 Reference Type
    所以 == 確實是在比較兩個變數是否指向同一個物件

    那你所問的「修改時不管改哪個名稱的字串都會修改到其內容嗎?」
    其實是不會的
    因為 String 的內容不可修改的(immutable)

    如果你今天寫 A = A + B;
    此時 C# 會將先分別將 A 跟 B 的字串內容抓出
    然後用陣列組合起來之後,再建立一個新的 String 物件並存入組合後的字串
    所以你最後的 A 會跟原本 A 指向不同的物件

    回覆刪除
  4. 原來如此,我了解了。
    而C++裡面必須呼叫字串的Compare函數來比較,C#卻如此方便。
    所以這種字串的比較模式算是C#特有的比較機制嗎?

    回覆刪除
  5. 不好意思,我發現我之前跟你提的「會指向同一個物件」這個概念不是很正確
    我後來又去官方網站查證了一下
    修正的內容我已經寫在上面的補充之下

    基本上,這個我們稱為 String Pool 的機制並非 C# 特有
    Java 也有一模一樣的機制
    但是 C# 在使用「==」來比較字串時,會特別地直接比較字串的「內容」
    而不是如我之前說的比較記憶體位置
    這種狀況 Java 就沒有!
    雖然我也不敢說是 C# 特有,但是就我所學的語言之中,確實是第一次看到

    回覆刪除
  6. 其實我一直很不了何時要宣告void跟return

    想了很久也問了人才瞭解,需要根據當時情況而定

    當時我認為一行就能判定了

    但發現如同內文所說會有負數的出現

    且這樣的方式並沒有維持單一職責的

    寫程式感覺難不再於寫出什麼 而是整個邏輯價購問題...

    回覆刪除