程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# 中幾個小“陷阱”

C# 中幾個小“陷阱”

編輯:C#入門知識

C# 中幾個小“陷阱”


 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   } 復制代碼     第一個foreach裡面會輸出3個true,這毫無疑問,第二個foreach裡面任然會輸出3個true?如果是的話,這個就不是陷阱了:-)         是不是很難理解這樣的結果,我也不懂:-(       繼續看下面的代碼:     復制代碼 1       var studentList = GetStudents().ToList(); 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()); 復制代碼     兩個都會輸出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中的計算表達式,把所有的對象都加載到集合中,這才是真正的集合。  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved