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',由於它公開外部可見的類型。數組
三、 CA1709 標識符的大小寫應當正確 更正程序集名稱 'TestBLL.dll' 中「BLL」的大小寫,將其改成「Bll」。(命名空間、類名等都是如此) 服務器
四、CA1707 標識符不該包含下劃線 從命名空間名稱「TestBLL.Class_Test」中移除下劃線。app
五、 CA1724 類型名不該與命名空間衝突 類型名 'ClassTest' 與命名空間名稱「TestBLL.ClassTest」總體或部分衝突。請更改其中任一名稱以消除衝突。編程語言
六、CA1305 指定 IFormatProvider 因爲 'int.ToString()' 的行爲可能會因當前用戶的區域設置不一樣而不一樣,請將 'ClassTest.Fun1()' 中的此調用替換爲對 'int.ToString(IFormatProvider)' 的調用。若是要向用戶顯示 'int.ToString(IFormatProvider)' 的結果,請指定 'CultureInfo.CurrentCulture' 做爲「IFormatProvider」參數。或者,若是軟件將存儲和訪問此結果(例如,當將此結果保留到磁盤或數據庫中時),則指定 'CultureInfo.InvariantCulture'。
七、CA1820 使用字符串長度測試是否有空字符串 使用「String.IsNullOrEmpty」調用來替換 'ClassTest.Fun1()' 中的 'string.operator ==(string, string)' 調用。
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」以保留該異常最初引起時所在的堆棧位置。
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
仔細觀察你會發現,第一種根本找不到具體異常的行,第二種雖然有具體異常信息,可是沒有行號,而第三種是最完善的,有具體異常信息,還有具體異常行號
九、CA1801 檢查未使用的參數 從未用過 'ClassTest.FunTest1(string, ref ClassTest2, int, string)' 的參數 'param3'。請移除該參數或在方法體中
十、CA1709 標識符的大小寫應當正確 在成員 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,更正參數名稱 'Param4' 中「Param」的大小寫,將其改成「param」。
十一、CA1045 不要經過引用來傳遞類型 考慮使用不須要將 'param2' 做爲引用參數的設計。
1 string param5 = ""; 2 param2 = new ClassTest2(); 3 param2.Age = 24; 4 param2.Fun1(param1, param4, ref param5);
那麼是時候考慮用ref了
十二、CA1062 驗證公共方法的參數 在外部可見方法 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,請先驗證局部變量「'(*param2)'」,而後再使用它,該變量是從參數「param2」從新分配而來的。
1三、CA1024 在適用處使用屬性 若是可行,請將 'Class_Test2.getTimeType()' 改成屬性
CA1822 將成員標記爲 static 從未使用 'Class_Test2.getTimeType()' 的「this」參數(Visual Basic 中爲「Me」)。根據須要,將成員標記爲 static (Visual Basic 中爲「Shared」),或者在方法體或至少一個屬性訪問器中使用「this」/「Me」。
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' 做爲引用參數的設計。
1五、CA1053 靜態容器類型不該具備構造函數 因爲類型 'Class_Test3' 僅包含「static」成員,所以將它標記爲「static」
1六、CA1815 重寫值類型上的 Equals 和相等運算符 'StructTest1<T>' 應重寫相等(==)和不等(!=)運算符。
1七、CA1002 不要公開泛型列表 更改 'StructTest1<T>.rows' 中的 'List<T>' 以使用 Collection<T>、ReadOnlyCollection<T> 或 KeyedCollection<K,V> TestBLL Class1.cs
剛開始還不太理解這個設計規範的意思,因而查了下資料,在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>中特有的屬性。
1八、CA1051 不要聲明可見實例字段 因爲字段 'StructTest1<T>.rows' 在其聲明類型的外部可見,所以,請將它的可訪問性改成私有,並添加一個與該字段當前的可訪問性相同的屬性以提供對該屬性的訪問。