編寫高質量程式碼改善C#程式的建議(Day2)
建議18:foreach不能代替for
在foreach迴圈內部不支援對集合進行增刪操作,FCL提供的迭代器內部維護了一個對集合版本的控制,增刪操作都會使版本號加1,foreach實現中呼叫MoveNext方法時會對版本號進行檢測,如果檢測到版本號有變動則會丟擲InvalidOperationException異常。
建議19:使用更有效的物件和集合初始化
Person person = new Person(){Name = "Lance", Age = 23}; List<Person> personList = new List<Person>() {new Person(){Name = "Jimmy", Age = 35}, person, null };
省略了通過構造方法傳值的步驟,但是初始化設定項更重要的作用是為LINQ查詢中的匿名型別進行屬性的初始化。LINQ查詢返回的集合中匿名型別的屬性都是隻讀的,如果需要為匿名型別屬性賦值只能通過初始化設定項來進行。
var pTemp = from p in personList select new { p.Name,AgeScope = p.Age > 20 ? "Old" : "Young" };
建議20:使用泛型集合代替非泛型集合
ArrayList不是型別安全的,並且效率上沒有List<T>高效。
建議21:選擇正確的集合
如果集合的數目固定並且不涉及轉型,使用陣列效率高,否則使用List<T>。
直接儲存指的是該型別的集合資料元素可以直接通過下標來訪問,新增元素直接把資料放在集合末尾就行了,插入元素就比較低效了,需要移動元素。
順序儲存無法通過下標索引查詢,它是通過對地址的引用來搜尋元素的,為了找到某個元素必須遍歷所有元素,優點是插入和刪除資料效率高。
建議22:確保集合的執行緒安全
在多執行緒應用環境下,可以通過Lock來保證執行緒安全,也可以使用Concurrent下的相關執行緒安全集合類。
建議25:謹慎集合屬性的可寫操作
如果類中有集合屬性,那麼應該保證屬性物件是由型別本身產生的,而不是外部直接賦值,在不知情情況下對外部引用物件修改時會影響到類中集合屬性,可以使集合屬性保持只讀,然後通過構造方法或者其他方式傳遞資料新增到集合屬性中。
建議26:使用匿名型別Var儲存LINQ查詢結果
匿名型別由var、賦值運算子、非空初始值組成(var x = null 會報錯)
匿名型別的屬性是隻讀的,沒有屬性設定器,一旦初始化就不可更改(指通過LINQ關鍵字查詢new出來的匿名型別);
如果兩個匿名型別的屬性值相同,那麼就認為兩個匿名型別相等;
實際開發中需求總是變化,需要根據需求建立很多臨時型別,如果都用普通自定義型別的話會增加程式碼量並且難以維護,匿名型別可以很好的解決這個問題。匿名型別預設過載ToString方法輸出型別的屬性名及對應的值。
建議27:在查詢中使用Lambda表示式
任何LINQ查詢都能通過呼叫擴充套件方法的方式來替代,針對LINQ設計的擴充套件方法大多應用了泛型委託。
Action用於執行一個操作沒有返回值;Func用於執行一個操作並返回值;Predicate用於定義一組條件並判斷引數是否符合條件;
// 把lambda寫在foreach查詢裡面不會影響效率,因為這是延遲求值,對查詢結果的訪問每次都會執行計算 foreach(var p in personList.Where(person=>person.CompanyID<20)) { Console.WriteLine(p.PersonName); }
建議28:理解延遲求值和主動求值之間的區別
List<int> list = new List<int>(){ 0,1,2,3,4,5,6,7,8,9}; var temp1 = from c in list where c>5 select c; var temp2 = (from c in list where c>5 select c).ToList<int>(); list[0] = 11; //查詢temp1時會計算linq的值,而temp2是立即執行的,所以修改list[0]對temp2已經沒有影響了
建議29:區別LINQ查詢中的IEnumerable<T>和IQueryable<T>
查詢本地資料來源用IEnumerable<T>,遠端資料來源用IQueryable<T>
AsEnumerable<T>也是延遲查詢,只不過是把遠端資料轉化成本地資料
DataContext ctx = new DataContext("server=192.168.1.84;database=IMS;uid=sa;pwd=123");
Table<TabStandard> standards = ctx.GetTable<TabStandard>();
var temp1 = (from s in standards where s.AvgPressure > 40 select s).AsEnumerable<TabStandard>();
var temp2 = from s in temp1 where s.RatePr > 0.8 select s;
foreach (var item in temp2)
{
Console.WriteLine(item.Indent.ToString() + " " + item.AvgPressure.ToString() + " " + item.RatePr.ToString());
}
在IQueryable<T>查詢的時候,無法使用自定義的方法,會拋異常
var temp3 = from s in standards where PressureLimit(s.AvgPressure) select s;
// 丟擲NotSupportedException異常
如果換成一個IEnumerable<T>查詢則不會
var temp3 = from s in temp1 where PressureLimit(s.AvgPressure) select s;
建議30:使用LINQ取代集合中的比較器和迭代器
var orderByBaseSalary = from s in companySalary orderby s.BaseSalary select s;