程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 編寫高質量代碼改善C#程序的157個建議[勿選List<T>做基類、迭代器是只讀的、慎用集合可寫屬性]

編寫高質量代碼改善C#程序的157個建議[勿選List<T>做基類、迭代器是只讀的、慎用集合可寫屬性]

編輯:C#入門知識

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要學習記錄以下內容:

  建議23、避免將List<T>作為自定義集合類的基類 

  建議24、迭代器應該是只讀的

  建議25、謹慎集合屬性的可寫操作

 如果要實現一個自定義的集合類,最好不要以List<T>作為基類,而應該擴展相應的泛型接口,通常是Ienumerable<T>和ICollection<T>(或ICollection<T>的子接口,如IList<T>。

  Employee1:List<Employee>
  Employee2:IEnumerable<Employee>,ICollection<Employee>

不過,遺憾的是繼承List<T>並沒有帶來任何繼承上的優勢,反而喪失了面向接口編程帶來的靈活性,而且可能不稍加注意,隱含的Bug就會接踵而至。

來看一下Employee1為例,如果要在Add方法中加入一點變化

       Name { ; 
      Employee1:List<Employee>  += 

進行調用

           Main(=  Employee(){Name= Employee(){Name= Employee(){Name=<Employee> employees = Employee(){Name= ( item 

結果竟然是這樣

這樣的錯誤如何避免呢,所以現在我們來來看看Employee2的實現方式

      Employee2:IEnumerable<Employee>,ICollection<Employee><Employee> items =  List<Employee> IEnumerator<Employee>
    }

這樣進行調用就是沒問題的

           Main(=  Employee(){Name= Employee(){Name= Employee(){Name=<Employee> employees = Employee() { Name =  ( item 

運行結果

 前端時間在實現迭代器的時候我就發現了這樣一個問題,迭代器中只有GetEnumeratior方法,沒有SetEnumerator方法。所有的集合也沒有一個可寫的迭代器屬性。原來這裡面室友原因的:

其一:這違背了設計模式中的開閉原則。被設置到集合中的迭代可能會直接導致集合的行為發生異常或變動。一旦確實需要新的迭代需求,完全可以創建一個新的迭代器來滿足需求,而不是為集合設置該迭代器,因為這樣做會直接導致使用到該集合對象的其他迭代場景發生不可知的行為。

其二:現在,我們有了LINQ。使用LINQ可以不用創建任何新的類型就能滿足任何的迭代需求。

關於如何實現迭代器可以來閱讀我這篇博文http://www.cnblogs.com/aehyok/p/3642103.html

現在假設存在一個公共集合對象,有兩個業務類需要對這個集合對象進行操作。其中業務類A只負責將元素迭代出來進行顯示:

            IMyEnumerable list = ==

業務類B出於自己的某種需求,需要實現一個新的針對集合對象的迭代器,於是它這樣操作:

            MyEnumerator2 enumerator2 = =

問題的關鍵就是,現在我們再回到業務類A中執行一次迭代顯示,結果將會是B所設置的迭代器完成輸出。這相當於BG在沒有通知A的情況下對A的行為進行了干擾,這種情況應該避免的。

所以,不要為迭代器設置可寫屬性。

 如果類型的屬性中有集合屬性,那麼應該保證屬性對象是由類型本身產生的。如果將屬性設置為可寫,則會增加拋出異常的幾率。一般情況下,如果集合屬性沒有值,則它返回的Count等於0,而不是集合屬性的值為null。我們來看一段簡單的代碼:

       Name { ;   Age { ;   List<Student> Students { ;  List<Student> list =  List<Student> Student(){Name=,Age= Student(){Name=,Age=  Main(= =  Thread(() =>=+=  Thread(() =>= 

首先運行後報錯了

這段代碼的問題就是:線程t1模擬將對類型StudentTeamA的Students屬性進行賦值,它是一個可讀/可寫的屬性。由於集合屬性是一個引用類型,而當前針對該屬性對象的引用卻有兩個,即集合本身和調用者的類型變量list。

  線程t2也許是另一個程序猿寫的,但他看到的只有list,結果,針對list的修改會直接影響到另一個工作線程中的對象。在例子中,我們將list賦值為null,模擬在StudentTeamA(或者說工作線程t1)不知情的情況下使得集合屬性變為null。接著,線程t1模擬針對Students屬性進行若干操作,導致異常的拋出。

下面我們對上面的代碼做一個簡單的修改,首先,將類型的集合屬性設置為只讀,其次,集合對象由類型自身創建,這保證了集合屬性永遠只有一個引用:

       Name { ;   Age { ;   List<Student> Students { ; =  List<Student> StudentTeamA(IEnumerable<Student> list): List<Student> list =  List<Student> Student(){Name=,Age= Student(){Name=,Age=  Main(=  Student() { Name=, Age=
            StudentTeamA teamB = 

修改之後,在StudentTemaA中嘗試對屬性Students進行賦值,就會發現如下問題

上面也發現了兩種對集合進行初始化的方式。

英語小貼士

1、I have an outing plan tommorrow。 ——明天我有一個徒步的計劃。

2、Amazing——使……人驚訝   unbelievable——令人不可思議

3、blue sky,white beach——藍天白雲

4、Could you please show me the way to cinema?——你可以向我展示去電影院的路嗎?

5、go straight——直走 turn left——向左轉 at the first cross——在第一個十字路口

作者:aehyok

出處:http://www.cnblogs.com/aehyok/

感謝您的閱讀,如果您對我的博客所講述的內容有興趣,那不妨點個推薦吧,謝謝支持:-O。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved