Effective C# Item23:避免返回內部類對象的引用

來源:互聯網
上載者:User

    當我們將屬性置為唯讀後,就可以認為調用者對該屬性的值不可以變更了嗎?答案是否定的。對於實值型別來說,將其置為唯讀,確實可以讓調用者不能夠對其進行修改;但是對於參考型別來說,調用者還是可以調用引用對象的公有成員,包括那些可以修改屬性狀態的成員。

    我們來看下面的代碼。

代碼

 1     public struct Employee
2 {
3 private string m_strName;
4 public string Name
5 {
6 get { return m_strName; }
7 set { m_strName = value; }
8 }
9
10 private int m_nAge;
11 public int Age
12 {
13 get { return m_nAge; }
14 set { m_nAge = value; }
15 }
16
17 public Employee(string name, int age)
18 {
19 m_strName = name;
20 m_nAge = age;
21 }
22
23 public override string ToString()
24 {
25 return string.Format("Name : {0}, Age : {1}.", m_strName, m_nAge);
26 }
27 }

    下面是測試代碼。

代碼

 1     private static void Test()
2 {
3 Employee emp = new Employee("Wing", 24);
4 Console.WriteLine(emp.ToString());
5 ChangeName(emp.Name);
6 ChangeAge(emp.Age);
7 Console.WriteLine(emp.ToString());
8 ChangeEmp(emp);
9 Console.WriteLine(emp.ToString());
10
11 }
12
13 private static int ChangeAge(int age)
14 {
15 age += 1;
16 return age;
17 }
18
19 private static string ChangeName(string name)
20 {
21 name = "UnKnown";
22 return name;
23 }
24
25 private static void ChangeEmp(Employee emp)
26 {
27 emp.Name = "Unkown";
28 emp.Age += 1;
29 }

    下面是執行結果。

    如果將上面代碼中Employee的類型由struct變為class,其他代碼不變,執行結果如下。

    通過上述代碼,我們可以看到,對於實值型別來說,在方法傳遞的過程中,是將對應的值進行複製後傳遞的;但是對於參考型別來說,是直接傳遞的引用對象的值。因此,通過調用方法對實值型別的改變並不會對原有實值型別的值,但是對於參考型別來說,就直接改變了原有引用對象的值。

    我們可以通過以下四種方式來防止類型內部的資料結構遭到無意的改變:

  1. 實值型別,就像上面代碼中的Age,屬於int類型。
  2. 常量類型,就像上面代碼中的Name,屬於string類型,對它的任何改變,都會直接建立一個新的string對象。
  3. 介面,在方法傳遞的過程中,不直接傳遞類的類型,而改用類實現的介面的類型,這樣可以降低調用方可以調用的方法,它只能調用介面中定義的成員。
  4. 封裝器,我們可以建立一個類,用於返回指定類型的資料,在返回的邏輯中,可以添加邏輯,對傳回值是否唯讀進行限制。

    上述四種方式中,前三種方式都已經在範例程式碼中進行說明了,我們主要看第四種方式,來看下面的代碼。

代碼

 1 public class MyBusinessObject
2 {
3 // Read Only property providing access to a
4 // private data member:
5   private DataSet _ds;
6 public IList this[ string tableName ]
7 {
8 get
9 {
10 DataView view =
11 _ds.DefaultViewManager.CreateDataView
12 ( _ds.Tables[ tableName ] );
13 view.AllowNew = false;
14 view.AllowDelete = false;
15 view.AllowEdit = false;
16 return view;
17 }
18 }
19 }
20
21
22 // Access the dataset:
23 IList dv = bizOjb[ "customers" ];
24 foreach ( DataRowView r in dv )
25 Console.WriteLine( r[ "name" ] );

    上述代碼中,結合了第三種方式和第四種方式,首先屬性的傳回值由DateView變成了IList,這樣調用方只能夠調用IList類型中定義的成員;其次,我們在返回IList之前,將DataView的AllowNew、AllowDelete和AllowEdit屬性都設定為false,這樣也能夠阻止調用方對其進行改動。

 

    總結:將參考型別通過公用介面暴露給外界,將使得類型的使用者不用通過我們定義的方法和屬性,就能夠更改對象的內部結構,這違反了我們通常的直覺,會導致常見的錯誤。如果我們到處的是引用而非值,那就需要改變類型的介面,如果只是簡單的返回內部資料,那麼我們實際上就給外界賦予了訪問內部成員的許可權。客戶代碼可以調用成員中任何可用的方法。通過使用介面或者封裝器對象向外界提供內部的私人資料,我們可以限制外界對它們的訪問能力。當希望客戶代碼更改內部資料元素時,我們應該實現Observer模式,以使對象可以對更改進行校正或響應。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.