Hi all,
其實最近一直在做WPF的項目,關於UI,MVVM,多線程,devexpress都想寫些東西,可惜忙着,雖然平時感悟挺深,也來不及整理,
也不想隨便寫寫忽悠大家,畢竟不是那種隨便寫寫都能寫出好文的大神,只是想好好的分享下項目過程中遇到的一些容易放錯的地方,俗話說
“知錯能改”嘛。
嘻嘻,其實這篇也是忽悠,只是今天在寫複雜function時遇到而已,一時沒注意就錯了都不知道。
以下僅個人理解加某些專業術語,有不對的地方請原諒。
“淺複製是對引用類型地址的複製,而深複製是對值類型的複製。”
這句話有兩個個名詞,
值類型:派生自System.ValueType的類型,我們常見的 int char long struct short 等等的數值或結構體或enum,
引用類型:派生自System.object的類型,如 string 數組array 類class list 等高級的數據類型。
文字不好說,還是直接上代碼說明:(一個極簡的CLI程序)
string aaa = "have";
string bbb = aaa;
bbb = bbb + "_lin";
Console.WriteLine(aaa);
這個例子有點特殊,故意舉例這個的,雖說string是引用類型,但是在等號複製的過程中還是“深複製”,具體原因查過資料才知道:
//表示空字符串。此字段爲只讀。
public static readonly string Empty;
答案就在於 string 是 readonly 的,當改變 string 類型的數據值時,將重新分配了內存地址。
我們舉另一個例子List類型:
List<string> arr1 = new List<string>();
arr1.Add("1");
arr1.Add("2");
arr1.Add("3");
var arr2 = arr1;
arr2[1] = "lin";
foreach(string s in arr1)
{
Console.WriteLine(s);
}
結果是:“1” “lin” "3" ,
上面的程序中,我們修改的是arr2,但arr1也同時被修改了,這說明arr1和arr2是使用一個值的,這就是引用類型的特點。
最後說明注意場景,例子僅自己今天遇到的,還是直接上代碼(有點長):
小結,在寫長代碼片段是,對臨時變量的管理特重要,在這個例子中,因爲要對新對象處理,對舊對象使用檢索,
所以不能讓新對象影響舊對象,看上去自然的邏輯,其實上錯在僅僅的一行代碼,這是很冤枉的。
best regards
希望能騰點時間來寫wpf的專題。
補充1:
很不幸,真的只能說是技術沒到家:。
對於List這樣的集合,提供了支持深複製的方法,GetRange,還是直接上代碼:
List<string> arr1 = new List<string>();
arr1.Add("1");
arr1.Add("2");
arr1.Add("3");
var arr2 = arr1;
changeList(arr2.GetRange(0, arr2.Count));
arr2[1] = "lin";
foreach (string s in arr1)
{
Console.WriteLine(s);
}
private static void changeList(List<string> list)
{
if (list.Count != 0)
{
list[0] = "haha";
}
}
最後結果是:‘1’ ‘lin’ ‘3’
代碼裏頭有兩處地方修改List,分別是index 0和1,但最終只有index 1改變,爲此,說明GetRange方法是對arr2對象的一個深複製。
回過頭來寫這一段,也是爲了指出之前犯的錯誤(CTO跟我說你那個函數遲早會有問題),我才痛定思痛的看了不知道多少遍,
直接上代碼說明:
Model層數據
//使用localDB連接數據庫獲取數據
public static Player[] GetPlayerByRegionId(Guid regionId)
{
using (var dbContext = new LocalModel())
{
var players = (from p in dbContext.Players where p.RegionId == regionId select p).ToArray();
return players;
}
}
Control層調用
private void Sample1(Guid regionId)
{
var players1 = LocalDBAccess.GetPlayerByRegionId(regionId);
List<Player> FirstListObj = new List<Player>(players1);
//接下來是複製對象部分,先舉錯誤示例,
//用我上面的方法獲取一個相同的對象,做法是這樣
var playeres2 = LocalDBAccess.GetPlayerByRegionId(regionId);
List<Player> LaterListObj = new List<Player>(playeres2);
//然後各自操作 FirstListObj ,,LaterListObj
.......
.......
}
在沒有訪問鎖(訪問限制)的數據庫中,在user1執行完 GetPlayerByRegionId 方法後,來自user2 給 相關表添加一條數據,
那麼用戶再次執行 GetPlayerByRegionId 方法時,他獲取到的 LaterListObj 就與FirstListObj 不一致,並沒有實現複製的功能。
下面改變Control層調用
private void Sample2(Guid regionId)
{
var players1 = LocalDBAccess.GetPlayerByRegionId(regionId);
List<Player> FirstListObj = new List<Player>(players1);
//接下來是複製對象部分,正確示例,
//使用GetRange方法,其他的集合對象也有類似的方法
var LaterListObj = FirstListObj.GetRange(0, FirstListObj.Count);
//然後各自操作 FirstListObj ,,LaterListObj
......
......
}
這裏我們對數據連接只有一次,所以數據庫的改變並不會馬上反應到變量中,所有操作均是在基礎數據上。
最後的最後,特意挖出來重新舉例,一是承認自己的錯,而勉勵自己繼續努力。
最後修改2017年3月31日11:15:39