Zmiana wartości elementu na liście struktur

Mam listę struktur i chcę zmienić jeden element. Na przykład:

MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

Teraz chcę zmienić jeden element:

MyList[1].Name = "bob"

Jednak za każdym razem, gdy próbuję to zrobić, dostaję następujący błąd:

Nie można modyfikować wartości zwracanej System.Kolekcje.Ogólne.Lista.to [int] " ponieważ nie jest zmienna

Jeśli używam listy klas, problem nie występuje.

Myślę, że odpowiedź ma związek ze strukturami będącymi wartością Typ.

Więc, jeśli mam listę struktur, czy powinienem traktować je jako tylko do odczytu? Jeśli muszę zmienić elementy na liście, to powinienem użyć klas, a nie struktur?

Author: Rohit Vipin Mathews, 2008-09-09

4 answers

MyList[1] = new MyStruct("bob");

Struktury w C# powinny być prawie zawsze zaprojektowane tak, aby były niezmienne (tzn. nie mają możliwości zmiany swojego stanu wewnętrznego po ich utworzeniu).

W Twoim przypadku, to co chcesz zrobić, to zastąpić całą strukturę w podanym indeksie tablicy, a nie próbować zmienić tylko jedną właściwość lub pole.

 29
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-16 11:00:54

Niezupełnie. Projektowanie typu jako klasy lub struktury nie powinno być podyktowane potrzebą przechowywania go w kolekcjach:) powinieneś spojrzeć na 'semantykę' potrzebną

Problem, który widzisz, wynika z semantyki typu wartości. Każda zmienna/Referencja typu wartości jest nową instancją. Kiedy mówisz

Struct obItem = MyList[1];

Dzieje się tak, że tworzona jest nowa instancja struktury, a wszystkie jej elementy są kopiowane jeden po drugim. Tak, że masz klon MyList[1] czyli 2 instancje. Teraz, jeśli zmodyfikować obItem, to nie wpływa na oryginał.

obItem.Name = "Gishu";  // MyList[1].Name still remains "peter"

Teraz wytrzymaj ze mną przez 2 minuty tutaj (to zajmuje trochę czasu, aby łyknąć w dół.. dla mnie tak. :) Jeśli naprawdę potrzebujesz struktur, które mają być przechowywane w kolekcji i modyfikowane, tak jak wskazałeś w pytaniu, musisz sprawić, by Twoja struktura wystawiała interfejs (, jednak spowoduje to Boks ). Następnie można zmodyfikować rzeczywistą strukturę za pomocą referencji interfejsu, która odnosi się do obiektu pudełkowego.

Poniższy fragment kodu ilustruje to, co właśnie powiedziane powyżej

public interface IMyStructModifier
{
    String Name { set; }
}
public struct MyStruct : IMyStructModifier ...

List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));

MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
    Console.WriteLine(s.Name);
}

HTH. Dobre Pytanie.
Update: @Hath - kazałeś mi biegać, aby sprawdzić, czy nie przeoczyłem czegoś tak prostego. (Byłoby to niespójne, gdyby właściwości settera nie działały , a metody - wszechświat. Net nadal jest zrównoważony :)
Setter method doesn ' t work
obList2[1] zwraca kopię, której stan zostanie zmodyfikowany. Oryginalna struktura w liście pozostaje niezmodyfikowana. Więc Set-via-Interface wydaje się być jedynym sposobem, aby to zrobić.

List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}
 39
Author: Gishu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-02 01:52:11

To nie tyle, że struktury są " niezmienne."

Prawdziwy problem polega na tym, że struktury są typem wartości, a nie typem odniesienia. Więc kiedy wyciągniesz" odniesienie " do struktury z listy, tworzy ona nową kopię całej struktury. Tak więc wszelkie zmiany, które na niej wprowadzasz, zmieniają kopię, a nie oryginalną wersję na liście.

Jak twierdzi Andrew, musisz zastąpić całą strukturę. W tym punkcie, chociaż myślę, że musisz zadać sobie pytanie, dlaczego używasz struct na pierwszym miejscu (zamiast klasy). Upewnij się, że nie robisz tego wokół przedwczesnych problemów optymalizacji.

 11
Author: Jason Olson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 22:22:50

Nie ma nic złego w strukturach, które mają odsłonięte pola, lub które pozwalają na mutację za pomocą seterów właściwości. Struktury, które mutują się w odpowiedzi na metody lub gettery właściwości, są jednak niebezpieczne, ponieważ system pozwoli na wywołanie metod lub getterów właściwości na tymczasowych instancjach struktury; jeśli metody lub gettery wprowadzą zmiany do struktury, zmiany te zostaną odrzucone.

Niestety, jak zauważyłeś, Kolekcje wbudowane w. Net są naprawdę słaba w eksponowaniu obiektów typu value w nich zawartych. Najlepiej zrobić coś w stylu:

  MyStruct temp = myList[1];
  temp.Name = "Albert";
  myList[1] = temp;

Trochę irytujące, i wcale nie threadsafe. Wciąż ulepszenie w stosunku do listy typu klasy, gdzie zrobienie tego samego może wymagać:

  myList[1].Name = "Albert";

Ale może również wymagać:

  myList[1] = myList[1].Withname("Albert");

A może

  myClass temp = (myClass)myList[1].Clone();
  temp.Name = "Albert";
  myList[1] = temp;
A może jakieś inne wariacje. Jeden naprawdę nie byłby w stanie wiedzieć, chyba że jeden zbadał myClass, a także drugi kod, który umieszcza rzeczy w lista. Jest całkowicie możliwe, że ktoś może nie być w stanie dowiedzieć się, czy pierwszy formularz jest bezpieczny bez zbadania kodu w zespołach, do których nie ma dostępu. Dla kontrastu, jeśli Name jest odkrytym polem MyStruct, metoda, którą podałem do aktualizacji będzie działać, niezależnie od tego, co jeszcze Zawiera MyStruct, lub niezależnie od tego, co inne rzeczy mogły zrobić z myList przed wykonaniem kodu lub czego mogą się spodziewać po jego wykonaniu.
 4
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-19 07:59:41