你不知道的經常使用 代碼分析 規範

visual studio有個功能,代碼分析,通常開發完畢後,除了處理常規的「錯誤列表」顯示的「錯誤」和「警告」,咱們更加應該注意的是,運行代碼分析功能,規範咱們的代碼,由於很差的編碼習慣,在沒有人指出和沒有團隊氛圍的開發中,不少時候都是一路不規範到底html

visual studio菜單的「分析」-》「對***運行代碼分析」或者在解決方案的類庫右擊選擇代碼分析
若是爲了強迫本身養成良好的c#微軟規範的習慣,咱們能夠右擊類庫屬性,找到最後一行標籤「代碼分析」,並在對應右側明細的「規則集」->"運行此規則集"下拉框中選擇Microsoft的全部規則。固然若是你須要每次生成代碼時讓vs自動幫咱們執行代碼分析,也能夠勾選上覆選框「生成時啓動代碼分析」,在長時間的編碼中若是每次都運行代碼分析,咱們的代碼會愈來愈規範和高效率
我找了之前的不少代碼和網上下載的代碼,以及公司的一些朋友的代碼,逐一代碼分析後,總結了以下常規開發中通常會遇到的規範問題,這些都不是錯誤或者警告,可是對於須要提升自身修養的程序員來講,這是必修課,固然本文只是拋磚引玉,更多的規範在微軟的官方文檔中都有,只是不少永遠不會遇到
MSDN:http://msdn.microsoft.com/zh-cn/library/dd264939(v=vs.100).aspx

下面我將最最最經常使用的規範問題,總結在一段程序當中(至關簡單的程序),朋友們能夠不運行代碼分析憑藉本身的經驗來判斷,到底有多少處不規範的地方
我敢保證,對於常規的要求不是很嚴格的開發,如下這些問題或多或少都會在您的代碼中出現
調用入口:程序員

 1 static void Main(string[] args)
 2         {
 3             try
 4             {
 5                 Class_Test test1 = new Class_Test();
 6                 test1.Fun1();
 7             }
 8             catch (Exception ex)
 9             {
10                 Console.WriteLine(ex.ToString());
11             }
12             Console.ReadLine();
13         }

                                                                                     (代碼1)web

核心代碼(爲了查看方便把多個類放到同一個文件):數據庫

 1 namespace TestBLL.Class_Test
 2 {
 3     public class Class_Test
 4     {
 5         public void Fun1()
 6         {
 7             const int param1 = 10;
 8             string name = param1.ToString();
 9             if (name == "")
10             {
11                 Console.WriteLine("empty");
12             }
13             else
14             {
15                 try
16                 {
17                     Class_Test2 test2 = new Class_Test2();
18                     test2.Age = 25;
19                     Fun_Test1("", ref test2, 100, "");
20 
21                     bool param2 = Boolean.Parse(name);
22                     Console.WriteLine(param2);
23                     List<string> list = new List<string>();
24                     list.Add(name);
25                 }
26                 catch (Exception ex)
27                 {
28                     Console.WriteLine(ex.Message);
29                     throw ex;
30                 }
31             }
32         }
33         public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)
34         {
35             string param5 = "";
36             param2.Age = 24;
37             param2.Fun1(param1, Param4, ref param5);
38 
39         }
40     }
41     public class Class_Test2
42     {
43         public int Age { get; set; }
44         public string getTimeType()
45         {
46             string TimeType = string.Empty;
47             int hour = DateTime.Now.Hour;
48             if (hour >= 1 && hour < 5)
49                 TimeType = "凌晨";
50             else if (hour >= 5 && hour < 11)
51                 TimeType = "早上";
52             return TimeType;
53         }
54         public void Fun1(string param, string param1, ref string param2)
55         {
56             # region
57             for (int i = 0; i < 10; i++)
58             {
59                 string name = "yhc";
60                 Console.WriteLine(name);
61             }
62             //此處省略99個如上for循環代碼塊
63             #endregion
64             Fun2();
65             Console.WriteLine(param);
66         }
67         private void Fun2()
68         {
69             Console.WriteLine();
70         }
71     }
72     public class Class_Test3
73     {
74         public static void Fun1()
75         {
76         }
77     }
78     public struct StructTest<T>
79     {
80         public List<T> rows;
81     }
82 }

                                                                            (代碼2)編程

代碼分析後有N個警告,大多數都要引發重視
一、CA2210 程序集應具備有效的強名稱 用強名稱密鑰對 'TestBLL.dll' 進行簽名。
  
c#

二、CA1014 用 CLSCompliantAttribute 標記程序集 使用 CLSCompliant(true)來標記 'TestBLL.dll',由於它公開外部可見的類型。數組

  • 若是您的程序集須要跨編程語言使用,而且您知道cls(公共語言規範)的概念,那麼您必須處理這條警告,由於必須 CLSCompliantAttribute 顯式指示 CLS 符合性,讓您的程序集在跨語言使用時不會出現衝突或者異常,好比vb中函數是不區分大小寫,若是您不處理這條警告,在編寫您的程序集時,您在代碼同一個Class中有兩個方法,只是大小寫不一樣,例如Fun和fun,那麼不會有任何警告和問題,可是被vb中調用就會報異常
  • 若是您須要使用CLSCompliantAttribute 來標記程序集,只須要在AssemblyInfo.cs文件中
    添加命名空間引用using System;並加上代碼[assembly:CLSCompliant(true)]便可
    詳細參考地址:http://www.cnblogs.com/mywebname/articles/598460.html
    http://msdn.microsoft.com/zh-cn/library/ms182156
  • 若是你以爲沒有必要,能夠在「操做」中選擇「禁止顯示此消息」

三、 CA1709 標識符的大小寫應當正確 更正程序集名稱 'TestBLL.dll' 中「BLL」的大小寫,將其改成「Bll」。(命名空間、類名等都是如此)   服務器

  • 咱們都知道Camel和Pascal命名規則,也會常常按照此規則編寫代碼,可是仍是會常常寫出相似TestBLL的命名代碼,這邊不少人忽略的是雙字母單詞,通常的vs分析會檢測您的命名中是否存在雙字母單詞(雙字母單詞固定就那麼多),若是存在則不會報錯好比:採用Pascal命名規範給程序集命名TestDB,那麼不會發出警告,由於DB是屬於雙字母單詞,若是採用Pascal命名規則,那麼通常三個或者三個以上字符是不容許出現所有大寫的,好比這裏的TestBLL中的"BLL"
  • 詳細參考地址:http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=ZH-CN&k=k(CA1709);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5)&rd=true

四、CA1707 標識符不該包含下劃線 從命名空間名稱「TestBLL.Class_Test」中移除下劃線。app

  • 在.NET平臺下,微軟不建議使用匈牙利命名法,因此在這裏有下劃線的存在也會拋出警告,後面的類名Class_Test二、Class_Test3,以及方法名Fun_Test1都是不規範的
  • 詳細參考地址:http://msdn.microsoft.com/library/ms182245(VS.100).aspx

五、 CA1724 類型名不該與命名空間衝突 類型名 'ClassTest' 與命名空間名稱「TestBLL.ClassTest」總體或部分衝突。請更改其中任一名稱以消除衝突。編程語言

六、CA1305 指定 IFormatProvider 因爲 'int.ToString()' 的行爲可能會因當前用戶的區域設置不一樣而不一樣,請將 'ClassTest.Fun1()' 中的此調用替換爲對 'int.ToString(IFormatProvider)' 的調用。若是要向用戶顯示 'int.ToString(IFormatProvider)' 的結果,請指定 'CultureInfo.CurrentCulture' 做爲「IFormatProvider」參數。或者,若是軟件將存儲和訪問此結果(例如,當將此結果保留到磁盤或數據庫中時),則指定 'CultureInfo.InvariantCulture'。

  • 代碼位置:代碼2中的8行,string name = param1.ToString();
  • 在數據具備邏輯性判斷或者存儲數據的時候,一些區域化參數必定不要忘記,幾乎全部時候咱們都是基本本國開發,歷來不會考慮這些參數,包括ToString(),ToUpper(),DateTime.ToShortDateString(),DateTime.ToString()等等,因此也歷來沒有問題,那是由於系統會選擇你當前線CultureInfo(和你強制加參數CultureInfo.CurrentCulture效果同樣)。雖然用戶界面UI看上去很智能,可是帶來的一個問題是:若是你的數據是純邏輯性判斷或者存儲的時候,請不要偷懶,由於邏輯判斷的值或者存儲起來的值是須要一個固定的不須要隨着區域而變化的值,不然服務端將各類異常奇怪現象,此時就要設置'CultureInfo.InvariantCulture',由於它是不依賴於區域性(固定)的 System.Globalization.CultureInfo 對象
  • 詳細參考地址:http://weishangxue.blog.163.com/blog/static/2157518820117193125196/
  • 修改成:string name = param1.ToString(CultureInfo.InvariantCulture);

七、CA1820 使用字符串長度測試是否有空字符串 使用「String.IsNullOrEmpty」調用來替換 'ClassTest.Fun1()' 中的 'string.operator ==(string, string)' 調用。

  • 代碼未知:代碼2中的9行,if (name == "" )
  • 判斷字符串是否爲空,請不要再用equals 或者== 「」了,由於字符串的equal或者==(操做符重載)都是重載過的,object的equal或者==都是比較是否引用相同,而string重載後則是比較值了,首先能夠肯定的是String.IsNullOrEmpty或者String.Length確定比equals或者==「」效率高,由於用ILDASM查看il代碼時,發現執行的il代碼行數前者比後者少,這是其次,最重要的是用ILSpy反編譯String類的Equals代碼後發現,內部作了太多操做
1 public bool Equals(string value)
2         {
3             if (this == null)
4             {
5                 throw new NullReferenceException();
6             }
7             return value != null && (object.ReferenceEquals(this, value) || (this.Length == value.Length && string.EqualsHelper(this, value)));
8         }
1 public static bool IsNullOrEmpty(string value)
2         {
3             return value == null || value.Length == 0;
4         }

 

八、CA2200 再次引起以保留堆棧詳細信息 'ClassTest.Fun1()' 再次引起捕獲的異常並將其顯式地指定爲一個參數。請改用不帶參數的「throw」以保留該異常最初引起時所在的堆棧位置。

  • 代碼位置:代碼2中的29行,throw ex;
  • 這邊有兩個問題,第一:捕捉異常最好不用直接用Exception類型來捕捉,由於這樣系統會遍歷全部異常信息來查找匹配的異常,致使的後果就是一旦發生異常,系統就會卡頓好久,最好的作法是由上到下先捕捉可能會發生的異常類型,最後再捕捉異常類型基類Exception,常見的異常類並非不少,若是有時候您根本不知道你應該寫什麼異常類型,那麼您先保存下經常使用的異常類列表,使用時再去查看,時間長了天然就知道了,以下:
    C#異常類1、基類Exception
    
    C#異常類2、常見的異常類
    
    1、SystemException類:該類是System命名空間中全部其餘異常類的基類。(建議:公共語言運行時引起的異常一般用此類)
    
    2、ApplicationException類:該類表示應用程序發生非致命錯誤時所引起的異常(建議:應用程序自身引起的異常一般用此類)
    
    C#異常類3、與參數有關的異常類
    
    此類異常類均派生於SystemException,用於處理給方法成員傳遞的參數時發生異常
    
    1、ArgumentException類:該類用於處理參數無效的異常,除了繼承來的屬性名,此類還提供了string類型的屬性ParamName表示引起異常的參數名稱。
    
    2、FormatException類:該類用於處理參數格式錯誤的異常。
    
    C#異常類4、與成員訪問有關的異常
    
    1、MemberAccessException類:該類用於處理訪問類的成員失敗時所引起的異常。失敗的緣由可能的緣由是沒有足夠的訪問權限,也多是要訪問的成員根本不存在(類與類之間調用時經常使用)
    
    2、MemberAccessException類的直接派生類:
    
    i、FileAccessException類:該類用於處理訪問字段成員失敗所引起的異常
    
    ii、MethodAccessException類:該類用於處理訪問方法成員失敗所引起的異常
    
    iii、MissingMemberException類:該類用於處理成員不存在時所引起的異常
    
    C#異常類5、與數組有關的異常
    
    如下三個類均繼承於SystemException類
    
    1、IndexOutOfException類:該類用於處理下標超出了數組長度所引起的異常
    
    2、ArrayTypeMismatchException類:該類用於處理在數組中存儲數據類型不正確的元素所引起的異常
    
    3、RankException類:該類用於處理維數錯誤所引起的異常
    
    C#異常類6、與IO有關的異常
    
    1、IOException類:該類用於處理進行文件輸入輸出操做時所引起的異常。
    
    2、IOException類的5個直接派生類:
    
    i、DirectionNotFoundException類:該類用於處理沒有找到指定的目錄而引起的異常。
    
    ii、FileNotFoundException類:該類用於處理沒有找到文件而引起的異常。
    
    iii、EndOfStreamException類:該類用於處理已經到達流的末尾而還要繼續讀數據而引起的異常。
    
    iv、FileLoadException類:該類用於處理沒法加載文件而引起的異常。
    
    v、PathTooLongException類:該類用於處理因爲文件名太長而引起的異常。
    
    C#異常類7、與算術有關的異常
    
    1、ArithmeticException類:該類用於處理與算術有關的異常。
    
    2、ArithmeticException類的派生類:
    
    i、DivideByZeroException類:表示整數貨十進制運算中試圖除以零而引起的異常。
    
    ii、NotFiniteNumberException類:表示浮點數運算中出現無窮打或者非負值時所引起的異常。
    異常類型

     第二:請不要throw ex,讓外部代碼再去Catch,而應該是throw,再讓外部代碼再去Catch,其實最好的作法是throw new Exception(ex.Message, ex),如下用ex.ToString()輸出異常明細(用於log保存或者異常明細跟蹤):

    throw ex:拋出新異常,重置堆棧原始異常點,在外部捕捉時根本查看不到具體異常行

    System.FormatException: 該字符串未被識別爲有效的布爾值。
       在 TestBll.ClassTest.Fun1() 位置 e:\測試\console\ConsoleApplication1\TestBLL\Class1.cs:行號 38
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\測試\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行號 18


    throw:拋出異常,外部捕捉時能夠看到具體異常行

    System.FormatException: 該字符串未被識別爲有效的布爾值。
       在 System.Boolean.Parse(String value)
       在 TestBll.ClassTest.Fun1() 位置 e:\測試\console\ConsoleApplication1\TestBLL\Class1.cs:行號 38
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\測試\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行號 18


    throw new Exception(ex.Message, ex):新建一個異常,而後將ex的堆棧異常信息做爲其內部異常(innerException)

    System.Exception: 該字符串未被識別爲有效的布爾值。 ---> System.FormatException: 該字符串未被識別爲有效的布爾值。
       在 System.Boolean.Parse(String value)
       在 TestBll.ClassTest.Fun1() 位置 e:\測試\console\ConsoleApplication1\TestBLL\Class1.cs:行號 29
       --- 內部異常堆棧跟蹤的結尾 ---
       在 TestBll.ClassTest.Fun1() 位置 e:\測試\console\ConsoleApplication1\TestBLL\Class1.cs:行號 37
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\測試\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行號 18

    仔細觀察你會發現,第一種根本找不到具體異常的行,第二種雖然有具體異常信息,可是沒有行號,而第三種是最完善的,有具體異常信息,還有具體異常行號

  • 詳細參考地址:http://blog.csdn.net/honkerhero/article/details/1699093
    http://www.cnblogs.com/JerryTian/archive/2012/09/24/2699459.html
  • 修改成:
                   try{
                    }
                    catch (FormatException ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new FormatException(ex.Message, ex);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new Exception(ex.Message, ex);
                    }

 九、CA1801 檢查未使用的參數 從未用過 'ClassTest.FunTest1(string, ref ClassTest2, int, string)' 的參數 'param3'。請移除該參數或在方法體中

  • 代碼位置:代碼2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)
  • 這個不用多說,就是歷來沒有使用過的參數或者變量,都刪除掉,通常你們不會特地的出現這種狀況,可是每每在傳遞參數特別多,加上往復的邏輯變動後,每每會忘記刪掉不用的參數,或者說趕進度,根本不會去仔細看哪些沒用的沒刪,最多見的狀況就是,書寫代碼時寫着寫着,忽然發現好像有另外一種更方便的寫法,而後沒有刪除乾淨以前的邏輯
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string Param4)


十、CA1709 標識符的大小寫應當正確 在成員 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,更正參數名稱 'Param4' 中「Param」的大小寫,將其改成「param」。

  • 代碼位置:代碼2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)的Param4
  • 這個不用多說,命名規範看上面第3點
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string param4)

 十一、CA1045 不要經過引用來傳遞類型 考慮使用不須要將 'param2' 做爲引用參數的設計。

  • 代碼位置:代碼2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)的Param4
  • 對於引用類型做爲參數傳遞時,其自己就是傳遞對象的引用(其實我更願意說是對象在堆的在內存中的地址),而Fun_Test1方法中對於Class_Test2類型的對象只是改變其屬性值(param2.Age = 24;)傳遞過來的指針指向的堆中的對象的首地址並無發生變化,因而對傳遞後的對象作任何操做均可以影響傳入以前的Class_Test2對象(前提是不new一塊內存來指向,或者指向別的內存塊),即在執行完畢後在外部Age也會變成24,這裏用了ref是畫蛇添足,由於對於引用類型若是還用ref的話,那麼傳遞的是該對象引用的引用(其實我更願意說是該對象在堆中內存地址的引用,即變量在棧中的地址),對於當前代碼的邏輯操做,是不須要這麼作的,由於在Fun_Test1方法中處理傳入的對象的時候並無從新new一個對象,若是Fun_Test1方法中代碼變成:
    1             string param5 = "";
    2             param2 = new ClassTest2();
    3             param2.Age = 24;
    4             param2.Fun1(param1, param4, ref param5);     

    那麼是時候考慮用ref了

  • 詳細參考地址:http://msdn.microsoft.com/library/ms182146(VS.100).aspx
    http://msdn.microsoft.com/zh-cn/library/s6938f28.aspx
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string param4)

 

 十二、CA1062 驗證公共方法的參數 在外部可見方法 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,請先驗證局部變量「'(*param2)'」,而後再使用它,該變量是從參數「param2」從新分配而來的。

  • 代碼位置:代碼2中的36行,param2.Age = 24
  • 引用類型的局部變量在使用前必須驗證是否爲空等,防止發生異常
  • 修改成:添加if (param2 != null){}判斷

1三、CA1024 在適用處使用屬性 若是可行,請將 'Class_Test2.getTimeType()' 改成屬性
       CA1822 將成員標記爲 static 從未使用 'Class_Test2.getTimeType()' 的「this」參數(Visual Basic 中爲「Me」)。根據須要,將成員標記爲 static (Visual Basic 中爲「Shared」),或者在方法體或至少一個屬性訪問器中使用「this」/「Me」。

  • 代碼位置:代碼2中的44行,public string getTimeType()
  • 仔細觀察:這種代碼在一個類中太常見了,以致於咱們都寫得飛起,編譯器代碼分析提示咱們是否能夠將其改成屬性或者靜態方法是有緣由的
     1  public string getTimeType()
     2         {
     3             string TimeType = string.Empty;
     4             int hour = DateTime.Now.Hour;
     5             if (hour >= 1 && hour < 5)
     6                 TimeType = "凌晨";
     7             else if (hour >= 5 && hour < 11)
     8                 TimeType = "早上";
     9             return TimeType += "";
    10         }

    改成屬性的緣由
    第一該方法getTimeType修飾符是public,而且沒有采用任何參數或返回的值不是數組,這符合屬性的要求,同時屬性大多數狀況下表明的是數據,方法表明的是執行操做,屬性在訪問更加方便,若是您的所謂的方法邏輯處理不是不少,不是很耗時,而且永遠不會由於調用次數的不一樣而產生不一樣的結果,那麼爲何不優先使用屬性呢?
    改成靜態方法的緣由:能夠將不訪問實例數據或不調用實例方法的成員標記爲 static,getTimeType內部沒有訪問任何其餘類實例的屬性或者實例方法,也沒有訪問當前所屬類的屬性或者字段等數據(沒有使用this),那麼它就是符合抽象類的條件,這裏不討論靜態方法和實例方法性能對比問題,只是代碼規範上說這個getTimeType方法不該該定義成實例方法,若是你把這樣一個十分經常使用的實例方法(其餘類也會用調用該方法)隨便定義在了一個類中,那麼你軟件的設計將是十分失敗,耦合度過高,牽一髮而動全身,而且從面向對象的方面來說,一個對象很是通用的功能爲什麼不定義成對象的基類的方法(若是全部調用類都只是從一個基類繼承)或者定義成公共類的靜態方法(若是全部調用類南轅北轍)

 1四、CA1045 不要經過引用來傳遞類型 考慮使用不須要將 'param2' 做爲引用參數的設計。

  • 代碼位置:代碼2中的54行, public void Fun1(string param, string param1, ref string param2)
  • 這條在第11條中已經描述過,但情景不一樣,我認爲該警告並不科學,能夠忽略,在咱們真的有這種需求ref 取得改變後的string類型的值時,用ref是能夠的,由於string雖然做爲引用類型,傳遞的是對象在堆中的地址,可是若是我改變param2的值,熟悉string機制的朋友知道,這時候會從新在堆中分配一塊內存,而後param2再指向這塊心的內存,到這裏爲止,所作的操做都和外部的那個string對象沒有任何關係,那麼使用ref後,傳遞的是該對象堆中地址的引用(通俗理解爲對象在棧的地址,),那麼你對param2所作的任何操做,其實都是在對外部的那個string對象操做,就算你再new出一塊堆上的內存
  • 不用修改,忽略代碼提示

 

1五、CA1053 靜態容器類型不該具備構造函數 因爲類型 'Class_Test3' 僅包含「static」成員,所以將它標記爲「static」

  • 代碼位置:代碼2中的72行, public class Class_Test3
  • 當前類所有都是靜態方法,爲什麼類型要定義成實例類?這樣在編譯時反而編譯器還會爲你生成默認的構造函數,這是沒有必要的,同時因爲類型沒有一個非靜態成員,即便建立實例也沒法提供對類型的任何成員的訪問
  • 修改成:public static class Class_Test3

 

1六、CA1815 重寫值類型上的 Equals 和相等運算符 'StructTest1<T>' 應重寫相等(==)和不等(!=)運算符。

  • 代碼位置:代碼2中的78行, public struct StructTest<T>
  • 首先您定義的結構(值類型)的代碼中有沒有用到兩個結構對象比較的地方,若是沒有,則忽略這條提示,若是有,那麼您必須重寫Equals等運算符, 對於值類型,Equals 的繼承的實現使用反射庫,並比較全部字段的內容。 反射須要消耗大量計算資源,可能沒有必要比較每個字段是否相等。 若是但願用戶對實例進行比較或排序,或者但願用戶將其做爲哈希表鍵,則值類型必須實現 Equals。 若是編程語言支持運算符重載,則還應提供等號和不等號運算符的實現
  • 酌情添加劇寫代碼,您能夠搜索如何重寫運算符

 

1七、CA1002 不要公開泛型列表 更改 'StructTest1<T>.rows' 中的 'List<T>' 以使用 Collection<T>、ReadOnlyCollection<T> 或 KeyedCollection<K,V> TestBLL Class1.cs

  • 代碼位置:代碼2中的79行, public List<T> rows;
  • 這個代碼分析提示也讓我很糾結,咱們一直用的List<T>怎麼就會不規範了,微軟說:「System.Collections.Generic.List<T> 是針對性能(而非繼承)設計的泛型集合。 System.Collections.Generic.List<T> 不包含更便於更改繼承類的行爲的虛擬成員。 下面的泛型集合是針對繼承功能設計的,應公開爲 System.Collections.Generic.List<T> 之外的內容」,在平時的使用中,我的以爲好像沒有什麼太大區別,有人測試過效率也相差無幾,並且不少綁定都是須要List<T>才能綁定
    終於找到一篇能解決疑惑的文章:
    剛開始還不太理解這個設計規範的意思,因而查了下資料,在Why we don’t recommend using List<T> in public APIs 一文中,簡要介紹了緣由:
    
    List<T>類型並非爲可擴展性而設計的,他優化了性能,可是丟失了可擴展性。好比,它沒有提供任何能夠override的成員。這樣就不能得到諸如集合改變時獲取通知的功能。Collection<T>集合容許咱們重寫受保護的SetItem方法,這樣當咱們向集合中添加或者修改集合中的記錄,調用SetItem方法的時候就能夠自定義一些事件來通知對象。
    List<T>對象有太多的與「場景」不相關的屬性和成員,將其做爲成員類型對外暴露在一些狀況下顯得過「重」,好比在WindowsForm中ListView.Items並無返回一個List對象,而是一個 ListViewItemCollection對象,該對象的簽名爲:
    // Summary:
    //     Represents the collection of items in a System.Windows.Forms.ListView control
    //     or assigned to a System.Windows.Forms.ListViewGroup.
    [ListBindable(false)]
    public class ListViewItemCollection : IList, ICollection, IEnumerable
    是一個實現ICollection接口的對象。
    
    還有在咱們經常使用的DataTable的Rows對象是一個DataRowCollection對象,該對象繼承自InternalDataCollectionBase
    
    // Summary:
    //Represents a collection of rows for a System.Data.DataTable.
    public sealed class DataRowCollection : InternalDataCollectionBase
    public class InternalDataCollectionBase : ICollection, IEnumerable
    而InternalDataCollectionBase則實現ICollection接口。
    
    public class InternalDataCollectionBase : ICollection, IEnumerable
    
    能夠看到微軟的.NET BCL中沒有直接暴露List類型的成員。該規則建議咱們使用Collection<T>。
    
    List<T>一般用來做爲類的內部實現,由於它對性能進行過優化,具備一些豐富的功能,而Collection<T>則是提供了更多的可擴展性。在編寫公共API的時候,咱們應該避免接受或者返回List<T>類型的對象,而是使用List的基類或者Collection接口。
    
    Collection<T>雖然能夠直接使用,可是一般做爲自定義集合的基類來使用,通常的咱們應該使用Collection<T>類型的對象來對外暴露功能,除非須要一些List<T>中特有的屬性。
    Collection和List的主要區別和使用場景

     

  • 酌情添加劇寫代碼,您能夠搜索如何重寫運算符
  • 在此給出連接:http://www.cnblogs.com/yangecnu/archive/2014/05/23/Do-not-expose-generic-lists.html
    http://www.cnblogs.com/netlife/archive/2009/05/21/1271597.html
    http://msdn.microsoft.com/library/ms182142(VS.100).aspx

 

1八、CA1051 不要聲明可見實例字段 因爲字段 'StructTest1<T>.rows' 在其聲明類型的外部可見,所以,請將它的可訪問性改成私有,並添加一個與該字段當前的可訪問性相同的屬性以提供對該屬性的訪問。

  • 代碼位置:代碼2中的79行, public List<T> rows;
  • 永遠不要聲明公開的字段,面嚮對象語言的封裝性決定,若是要公開,請用屬性
  • 修改成屬性
相關文章
相關標籤/搜索