每天寫代碼,偶爾就會有讓你抓狂的時候:代碼改了千百遍,驀然回首,Bug就在燈火闌珊處……這里就列舉一些容易犯錯的幾個小地方,以后遇到了其他的,再慢慢添加。

1. 獲取程序當前運行路徑
情景復現:WPF客戶端程序,開機自啟動后無法進入主界面,卡在初始屏(Splash Screen)
處理問題:通過日志發現加載一個icon的時候,跳了一個Bug。初始代碼如下:
很簡單,貌似不會有問題,相對目錄且正確。直接雙擊程序啟動完全正常,Debug啟動同樣完全正常,否則早就發現這個Bug了。開機自啟動時日志中的錯誤是:找不到“C:\Windows\System32\Images\xxx.ico”這個文件 ??? 這很讓人摸不着頭腦,程序中的相對目錄怎么會跑到sysem32里面了?目錄不對導致文件找不到,當然就進入到Exception里面了。
第一反應是相對目錄可能不帶靠譜,就改成了下面的代碼:
// var icon = new Icon(Environment.CurrentDirectory + "\\Images\\xxx.ico");
呵呵,還是不起作用,換一種寫法(被注釋的第二句),報的錯是一樣的。兩個方法返回的都是“C:\Windows\System32”這個路徑,在程序開機自啟動的時候。其實Environment.CurrentDirectory內部調用的也是Directory.GetCurrentDirectory()方法。
解決方案:StackOverflow上面關於這個問題有個討論,WinForm中Application.StartupPath也會有相同的問題,下面的是獲取當前目錄的推薦寫法:
2. IEnumerable之LINQ 表達式
軟件設計有個很重要的原則,就是高內聚,低耦合,於是我們經常的會面向接口編程,並且盡可能開放底層接口。所以IEnumerable就會經常用到,因為Linq操作中很多方法的返回值都是IEnumerable<T>。這一個陷阱就是關於IEnumberable,先看下面的代碼,看看返回值和你想的是不是一樣:

2 {
3 private static void Main( string[] args)
4 {
5 // ...
6 var students = GetStudents();
7 foreach ( var student in students)
8 {
9 student.IsActived = true;
10 Console.WriteLine(student.IsActived);
11 }
12
13 foreach ( var student in students)
14 {
15 Console.WriteLine(student.IsActived);
16 }
17
18 }
19
20 private static IEnumerable< string> GetNames()
21 {
22 // ....
23 return new[] { " AAA ", " BBB ", " CCC " };
24 }
25
26 private static IEnumerable<Student> GetStudents()
27 {
28 // ...
29 return GetNames().Select(s => new Student(s));
30 }
31
32 public class Student
33 {
34 public string Name { get; set; }
35 public bool IsActived { get; set; }
36
37 public Student( string name)
38 {
39 Name = name;
40 }
41 }
42
43 }
第一個foreach里面會輸出3個true,這毫無疑問,第二個foreach里面任然會輸出3個true?

如果你理解這樣的結果,那么這個“陷阱”對你無效,你可以跳過這一條了……
繼續看下面的代碼:

2 studentList[ 0].IsActived = true;
3 var studentsActived = studentList.Where(s => s.IsActived);
4
5 Console.WriteLine(studentsActived.Count());
6
7 studentList[ 0].IsActived = false;
8
9 Console.WriteLine(studentsActived.Count());
這次會輸出什么?

If(IsUnderstandingAgain) return; else……這里先不解釋,我們用代碼說話,繼續看代碼,修改一下GetStudents()方法:
2 {
3 // ...
4 return GetNames().Select(s =>
5 {
6 var stu = new Student(s);
7 Console.WriteLine(s + " : " + stu.GetHashCode());
8 return stu;
9 });
10 }
在上面的代碼中,GetStudent()方法被調用了2次,你覺得現在HashCode會輸入幾次?
