C# 中幾個小“陷阱”

來源:互聯網
上載者:User

標籤:winform   style   blog   http   color   使用   

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

  1. 擷取程式當前運行路徑

  情景複現:WPF用戶端程式,開機自啟動後無法進入主介面,卡在初始屏(Splash Screen)

  處理問題:通過日誌發現載入一個icon的時候,跳了一個Bug。初始代碼如下:

var icon = new Icon("Images\\xxx.ico");

    很簡單,貌似不會有問題,相對目錄且正確。直接雙擊程式啟動完全正常,Debug啟動同樣完全正常,否則早就發現這個Bug了。開機自啟動時日誌中的錯誤是:找不到“C:\Windows\System32\Images\xxx.ico”這個檔案 ??? 這很讓人摸不著頭腦,程式中的相對目錄怎麼會跑到sysem32裡面了?目錄不對導致檔案找不到,當然就進入到Exception裡面了。

    第一反應是相對目錄可能不帶靠譜,就改成了下面的代碼:

var icon = new Icon(Directory.GetCurrentDirectory() + "\\Images\\xxx.ico");
//var icon = new Icon(Environment.CurrentDirectory + "\\Images\\xxx.ico");

    呵呵,還是不起作用,換一種寫法(被注釋的第二句),報的錯是一樣的。兩個方法返回的都是“C:\Windows\System32”這個路徑,在程式開機自啟動的時候。其實Environment.CurrentDirectory內部調用的也是Directory.GetCurrentDirectory()方法。

   解決方案:StackOverflow上面關於這個問題有個討論,WinForm中Application.StartupPath也會有相同的問題,下面的是擷取目前的目錄的推薦寫法: 

var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

  2. IEnumerable應該正確的使用

    軟體設計有個很重要的原則,就是高內聚,低耦合,於是我們經常的會面向介面編程,並且儘可能開放底層介面。所以IEnumerable就會經常用到,因為Linq操作中很多方法的傳回值都是IEnumerable<T>。這一個陷阱就是關於IEnumberable,先看下面的代碼,看看傳回值和你想的是不是一樣:

 1   internal class Program
 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   }View Code

    第一個foreach裡面會輸出3個true,這毫無疑問,第二個foreach裡面任然會輸出3個true?如果是的話,這個就不是陷阱了:-)

    是不是很難理解這樣的結果,我也不懂:-(

    繼續看下面的代碼:

1       var studentList = GetStudents().ToList();
2       studentList[0].IsActived = true;
3       var studentsActived = studentList.Where(s => s.IsActived);

5       Console.WriteLine(studentsActived.Count());

7       studentList[0].IsActived = false;

9       Console.WriteLine(studentsActived.Count());View Code

    兩個都會輸出1 ?

    有沒有一種世界被顛覆的感覺……這裡先不解釋,我們用代碼說話,繼續看代碼,修改一下GetStudents()方法:

 1     private static IEnumerable<Student> 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會輸入幾次,6次?

 

 

     輸出結果是9次。地區3裡面的3次是由於調用GetStudents().ToList()方法,地區1和2則是由前面的兩個foreach運行時輸出的,而且每一次HashCode都不一樣,說明每一個都是不同的執行個體。再聯想一想Entity Framewor裡面是不是有一個Lazy Loading,每一次使用集合中的某個對象,就會執行一次SQL,從資料庫中尋找該對象。 真相就在這裡:IEnmerable 可以理解為只儲存了集合的計算運算式,在使用的集合裡面的對象時,會根據計算尋找該對象。由於GetStudents()函數是用Select方法,所以每次在使用的時候都會重新的New一次,這就是上面每一個HashCode都不一樣的原因。Linq中ToList()拓展方法就相當於執行IEnumerable中的計算運算式,把所有的對象都載入到集合中,這才是真正的集合。

 

    所以IEnumerable應該正確的使用,作為介面中的傳回值是很好的解耦方法,但是具體實現函數中的傳回值最好是一個集合類,如List<T>。

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.