2013-02-18 26 views
8

Wyobraź to struct:Nie można zmienić wartości członków struct jest wewnątrz kolekcji generycznych

 struct Person 
     { 
      public string FirstName { get; set; } 
      public string LastName { get; set; } 
     } 

I poniższy kod:

 var list = new List<Person>(); 
     list.Add(new Person { FirstName = "F1", LastName = "L1" }); 
     list.Add(new Person { FirstName = "F2", LastName = "L2" }); 
     list.Add(new Person { FirstName = "F3", LastName = "L3" }); 

     // Can't modify the expression because it's not a variable 
     list[1].FirstName = "F22"; 

Gdy chcę zmienić Property „s wartość daje mi następujący błąd :

Can't modify the expression because it's not a variable 

podczas, gdy próbowałem go zmienić wewnątrz tablicy su ch jak Person[] działało bez żadnego błędu. Czy jest jakiś problem z moim kodem, gdy używam generycznych kolekcji?

+0

Dlaczego twoja osoba nie ma klasy? –

+1

Posiadanie struktury zmiennej nie jest dobrym pomysłem - nie możesz użyć klasy? – Jay

+0

Może być, ale jestem ciekaw przyczyny tego błędu. – saber

Odpowiedz

13

Po zwrocie struct za pomocą indeksera zwraca on kopię egzemplarza pozycji. Jeśli więc przypisałeś tam numer FirstName, zostanie on wyrzucony. Stąd błąd kompilatora.

Albo przepisać Person być typ referencyjny class, czy pełny zmiany przeznaczenia:

Person person = list[1]; 
person.FirstName = "F22"; 
list[1] = person; 

Generalnie Zmienne kodowanym przynieść kwestii, takich jak te, które mogą powodować bóle głowy w dół drogi. Jeśli nie masz naprawdę dobrego powodu, aby z nich korzystać, powinieneś zdecydowanie rozważyć zmianę swojego typu Person.

Why are mutable structs “evil”?

+2

+1 Podejście typu "odtwórz/zmień/zapisuj", o którym mówisz, jest najczystszym sposobem na zakodowanie tego kodu, ponieważ bardzo wyraźnie widać, jaka część struktury została zmieniona. Kod taki jak 'list [1] = new Person (" F22 ", list [1] .LastName);' jest o wiele mniej czytelny i bardziej prawdopodobny, że jest błędny [np. jeśli 'Osoba' ma opcjonalny parametr' MiddleName', ten drugi kod przypadkowo wyczyści go - oops]. – supercat

0

przerobić struct jako takie:

struct Person 
    { 
     private readonly string firstName; 
     private readonly string lastName; 
     public Person(string firstName, string lastName) 
     { 
      this.firstName = firstName; 
      this.lastName = lastName; 
     } 
     public string FirstName { get { return this.firstName; } } 
     public string LastName { get { return this.lastName; } } 
    } 

I następujący kod jako:

var list = new List<Person>(); 
    list.Add(new Person("F1", "L1")); 
    list.Add(new Person("F2", "L2")); 
    list.Add(new Person("F3", "L3")); 

    // Can modify the expression because it's a new instance 
    list[1] = new Person("F22", list[1].LastName); 

Wynika to semantyki egzemplarzy struct. Uczyń go niezmiennym i pracuj w tych ograniczeniach, a problem zniknie.

+1

Nie podoba mi się podejście "niezmienne", ponieważ trzeba zbadać kod struktury, aby wiedzieć, jaki wpływ ma "lista [1] = nowa osoba (" F22 ", lista [1] .LastName);' jest. Jeśli na przykład klasa "Person" zawiera pole 'MiddleName', które zostanie zainicjowane przez opcjonalny parametr' MiddleName', powyższa instrukcja (prawdopodobnie przypadkowo) usunie 'list [1] .MiddleName'. Natomiast jeśli "LastName" jest eksponowanym * polem *, wiedząc, że to wystarczy, by stwierdzić, że kod w odpowiedzi Chrisa Sinclaira nie wpłynie na cokolwiek innego. – supercat

4

Oczywiście część pytania pozostaje bez odpowiedzi. Jaka jest różnica między List<Person> i Person[]. W przypadku uzyskiwania elementu za pomocą indeksu wywoływacz wywołania (metoda) List, który zwraca kopię instancji typu wartości, w przeciwnej tablicy przez indeks zwraca nie kopię, ale zarządzany wskaźnik do elementu w indeksie (wykorzystano specjalną instrukcję IL ldelema).

Oczywiście zmienne typy wartości są złe, jak wspomniano w innych odpowiedziach. Spójrz na prosty przykład.

var en = new {Ints = new List<int>{1,2,3}.GetEnumerator()}; 
while(en.Ints.MoveNext()) 
{ 
    Console.WriteLine(x.Ints.Current); 
} 

Zaskoczony?