遇到一個bug,抓耳撓塞好久都沒有解決,有必要記錄一下。
現在我使用了一個多維list。
IList<IList<int>> list = new List<IList<int>>();
我在main函數中調用了方法函數,在方法函數中使用list.add()方法向list中添加sublist。添加代碼如下:
IList<int> l = new List<int>(); for(int i=0;i<10;i++) { if (條件) { l.clear(); list.Add(sublist); } }
一共添加了3個元素
但是最終的list中,卻始終只有第三個sublist,前兩個sublist存在,但是為空。這就非常奇怪,既然前兩個元素存在,那么就代表list.Add(sublist)語句成功運行了,但為什么值為空呢?
我第一時間就懷疑Add方法會覆蓋掉之前的值,於是我又在main函數中手動添加了兩個sublist,打印出來發現,后面添加的都能成功寫入,只有循環內的不可以。
這段代碼是在vscode里面寫的,由於不能單步調試查看局部變量,耽擱了好多時間。於是我把所有代碼移到了VS2017上,開始我的單步調試(VS2017大法好!)
我一步一步調試,發現第一個sublist寫入是成功的,到了第二個sublist要寫入的那一刻,刷!list[0]的值變成了空!
現在問題找到了,但是我不明白問題出在哪里。
在網上終於搜到了解決方法。原來是在循環中如果使用同一個sublist,那么每次add都會把之前覆蓋掉。解決辦法就是不要在循環外定義sublist,而是在循環內部定義。
該方法給出的解釋如下:
1.對於引用類型,在循環外new了 a 對象后,這個對象的引用地址就確定了,執行到第二次list.add()時,list[0]中保存的a對象和新加的list[1] a對象是同一個對象,使用的是同一個地址,也就是說在添加list[1]是,list[0]也被修改了,因為它倆指針指向同一個地址,同樣,后面添加的都會修改前面所有對象,結果就是list最后所有數據都是最后的list[end_num]。
2.string也是引用對象,有唯一的引用地址(假設分配的是address1),但當add list[1]的時候(str值改變時),.net會檢查內存,發現不等於之前的字符串(list[1]不等於list[0]),.net會給原來的str重新分配內存空間address2,存儲list[0],而list[1]使用原來的空間address1。這樣使得每次list.add時都不會覆蓋之前的元素,因為它們使用的是不同的地址空間。
————————————————
版權聲明:本文為CSDN博主「IT登山家」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_35409640/article/details/71404203
但是呢,我在進一步調試中發現兩個問題:
1.如果去除l.clear(),程序是正常的,list值全部正常,只有添加了l.clear(),list的前幾個值才會被抹去。
2.在main函數中加這樣的循環(包含l.Clear()),list可以正常寫入
結合該方法,我認為覆蓋的原因如下:
現在申請的所有類型在.net中都被認為是引用對象,當add的時候,.net會統一重新分配內存。但是在add之前,l都指向了l的地址,查了資料就知道,clear並不清除list內存,想必是只擦除了地址,所以才會既沒有報錯,也沒有值。
至於在main函數中為什么不會發生這種現象,我還沒有比較好的思路,可能是因為list是在main中一定,不需要引用函數,也就沒有list的引用過程,可能add方法就直接寫入list了。水平有限,歡迎有識之士補充。