2015-04-01 111 views
0

例如我有以下類:意外行爲

class Person 
{ 
    public List<int> Grades{ get; set; } 

    public Person(List<int> grades) 
    { 
     this.Grades = grades; 
    } 
} 

而且比我使用這個類的主要方法,是這樣的:

static void Main(string[] args) 
    { 
     List<int> grades = new List<int> { 1, 2, 3, 4, 5 }; 
     Person person = new Person(grades); 

     for (int i = 0; i < 5; i++) 
     { 
      if (i % 2 == 0) 
      { 
       grades[i] = 0; 
      } 
      else 
      { 
       grades[i] = -1; 
      } 
     } 

     foreach(var grade in person.Grades) 
     { 
      Console.Write(grade + " "); 
     } 

     Console.ReadKey(); 
    } 

運行之後代碼我預計在控制檯上:輸出結果。 Insteead我有這個輸出結果: 0 -1 0 -1 0

我希望集合「保存」到個人實例,以保持它的初始化。我應該怎麼做才能保持這個集合不被修改爲Person實例,這樣即使我修改了main方法中的等級集合,我也會得到「1 2 3 4 5」輸出結果。

有人可以請解釋我,爲什麼會發生這種情況?

+0

您存儲_reference_到列表中,而不是名單本身。 – 2015-04-01 19:05:05

回答

5

我期望集合「保存」到Person實例中,以保持其初始化方式。

現在是時候重新審視你的引用類型在C#中是如何工作的期望呢:)我有一個article,你可能會發現有用...

我應該做些什麼來保持這個集合未修改Person實例,這樣即使我修改main方法中的等級集合,我也會得到「1 2 3 4 5」輸出結果。

您可以複製集合中的構造函數:

public Person(List<int> grades) 
{ 
    this.Grades = new List<int>(grades); 
} 

現在的List<int>這很好 - 如果你有一個可變類型的列表,但是,您可能要克隆每個元素太。這種事情是爲什麼不可變的類型是好的 - 關於他們的行爲的理由要容易得多,因爲沒有東西可以混淆實例,所以你可以保持引用...

2
this.Grades = grades; 

上面一行僅分配由grades保持到this.Grades所以現在都指向/引用在存儲器中的相同項的引用。

要解決它,因爲它是一個List<int>,你可以做

this.Grades = grades.ToList(); 

但如果你有List<T>,其中T是一個類或引用類型,你仍然有同樣的問題。請參閱:How create a new deep copy (clone) of a List<T>?

+2

這不是一個真正的淺拷貝,它只是對同一個對象的另一個引用。 – Blorgbeard 2015-04-01 19:05:02

2

這兩個局部變量grades和字段Person類都引用相同的列表。對任何一個變量賦值都會導致返回相同的列表,因此會改變存在的一個列表創建了可以從任一變量中觀察到的變化。

如果您在構造函數中指定屬性的值時創建一個新列表並將其中的所有值複製到新列表中,那麼您將擁有兩個單獨的列表,並且通過另一個列表進行變異不會是可觀察的。

1

解釋是參考對象。

當您通過列表新人對象,你真正做的是一個指針傳遞到列表,因此列表中的人與本地列表變量grades實際上是相同的列表。

你可以有人物對象創建,而不是使用這兩種方法的列表的副本:

this.Grades = grades.ToList(); 
// or 
this.Grades = new List<int>(grades);