2013-12-23 19 views
1

我有一個變量temp,它的類型爲List<Point>。由於某種原因,當我撥打sanitize(temp)時,變量temp似乎發生了變化。我知道變量temp會發生變化,因爲如果我包含sanitize(temp)行,我在C:\out.bmp上得到的輸出是不同的。我從消息框中得到的輸出是「temp沒有改變。」爲什麼我的變量List <Point>被改變,即使我沒有使用引用?

List<Point> original = temp; 
sanitize(temp); 

if (temp.Equals(original)) { 
    MessageBox.Show(@"temp was not changed."); 
} else { 
    MessageBox.Show(@"temp was changed."); 
} 

outputPointsOnBitmap(temp.ToArray(), ref windowBitmap, Color.Yellow); 
windowBitmap.Save("C:\\out.bmp"); 

對於好奇,這裏有更多的源代碼:

private void outputPointsOnBitmap(Point[] points, ref Bitmap bitmap, Color markerColor) { 
    foreach (Point point in points) { 
     bitmap.SetPixel(point.X, point.Y, markerColor); 
    } 
} 

private List<Point> sanitize(List<Point> crossPoints) { 
    SortedSet<int> indexesToDelete = new SortedSet<int>(); 

    for (int i = 0; i < crossPoints.Count() - 1; i++) { 
     if (Math.Abs(crossPoints[i + 1].X - crossPoints[i].X) <= 5 && 
      Math.Abs(crossPoints[i + 1].Y - crossPoints[i].Y) <= 5) { 
      indexesToDelete.Add(i); 
      indexesToDelete.Add(i + 1); 
     } 
    } 

    foreach (int i in indexesToDelete.Reverse()) { 
     crossPoints.RemoveAt(i); 
    } 

    return crossPoints; 
} 

這是推動我堅果。

+1

您*使用參考。你正在通過* value * yes。但是您傳遞的價值是*列表*的參考*。 – aquinas

+0

@aquinas:我如何知道我是否使用對象或對象本身的引用?我想所有的列表只是對象的引用?如果我有一個'Point'類型的變量會怎麼樣?那是一個對象還是對象的引用? – user3130149

+0

C#具有引用類型和值類型。您應該閱讀以下內容:http://msdn.microsoft.com/en-us/library/490f96s2.aspx以獲得更好的理解。 – aquinas

回答

6

當你這樣做:

List<Point> original = temp; 

你把這些變量original參考列表temp指。

因此,sanitizetemp所做的任何更改也在original中可見。 temp.Equals(original)返回true,因爲它們是同一個對象。如果你想original成爲一個新的列表具有相同的項目,如temp,構造一個新的List對象:

List<Point> original = new List<Point>(temp); 

你說你不使用引用,但List<T>是引用類型,因爲它不繼承從ValueType - 當你分配或傳遞它時,你總是使用引用。

下面是淨價值和引用類型之間的區別的一個很好的細分: http://www.albahari.com/valuevsreftypes.aspx

由於約迪蘭根指出,temp.Equals(original)將始終返回false現在,因爲兩個列表是不同的對象(這就是什麼Object.Equals看參考類型)。如果你想看到的sanitize功能是否發生任何變化的列表,你可以使用Enumerable.SequenceEqual

if (temp.SequenceEqual(original)) { 
    MessageBox.Show(@"temp was not changed."); 
} else { 
    MessageBox.Show(@"temp was changed."); 
} 
+0

Equals方法現在總是返回false。 –

+0

一切從對象繼承,不僅僅是引用類型。 – fejesjoco

+0

是的。我會改變它說它*不*從ValueType繼承。 – babbageclunk

1

temp因爲您將引用傳遞給sanitize而引起變化,該變量被分配給crossPoints變量。因爲它通過引用傳遞,所以對crossPoints的任何更改(通過調用RemoveAt)也會影響temp,因爲它們指向相同的列表。

當你說:

List<Point> original = temp; 

你讓temporiginal refence相同的列表。它不創建列表的副本。

2

如果你想original不改變,這樣做:

List<Point> original = new List<Point>(temp); 
sanitize(temp); 

這將創建一個新的List<Point>temp的內容。否則,List<Point> original = temp只是創建一個新的引用(原始)到包含您的列表(temp)的同一個內存位置。

爲了檢查你的2個列表是否與你的情況不同,請檢查它們的Count(因爲Equals總是返回false)。

if(temp.Count != original.Count) 
{ 
    // now you know that some items were removed in your sanitize method 
} 
1

Temp和original不是對象,它們是對象的引用,在你的情況下,它們都引用相同的列表對象。通常情況下,equals會對對象進行引用比較,所以它會說它們是相等的。無論哪種方式,你都在比較一個列表對象本身。在您的清理方法中,您更改了相同的清單。

1

它正在被修改,因爲它被作爲參考傳遞,所以刪除操作實際上是在temp變量保存的同一個引用上執行的。你可能是期望的結果,如果你做到以下幾點:

private List<Point> sanitize(List<Point> crossPoints) { 
    var workset = new List<Point>(crosspoints); 
    SortedSet<int> indexesToDelete = new SortedSet<int>(); 

    for (int i = 0; i < workset.Count() - 1; i++) { 
     if (Math.Abs(workset[i + 1].X - workset[i].X) <= 5 && 
      Math.Abs(workset[i + 1].Y - workset[i].Y) <= 5) { 
      indexesToDelete.Add(i); 
      indexesToDelete.Add(i + 1); 
     } 
    } 

    foreach (int i in indexesToDelete.Reverse()) { 
     workset.RemoveAt(i); 
    } 

    return workset; 
} 
當然

記住這是一個O(n)的操作,因爲你是在列表中

1

這種說法使得引用的副本:

List<Point> original = temp; 

original確實現在有相同引用作爲temp。含義:適用於temp變量的所有更改也適用於original變量,因爲它們共享相同參考號

您應該創建一個deep copy的列表來完成這項工作。還有一件事:

如果您使用深度複製,Equals將始終返回false。等於檢查引用是否等於。您最有可能需要根據列表中的項目進行檢查。

0

您的列表temp是一個對象,並且對象通過引用傳遞,所以您的sanitize方法正在更改基礎對象。

然而,你的輸出表示,由於分配original = temp它沒有改變,這意味着您的變量original是指向同一個對象作爲變量temp,所以當你比較兩個,他們是平等的,即使對象實際上已經自從它通過消毒之前發生了變化。

相關問題