Å endre verdien av et element i en liste over structs

stemmer
53

Jeg har en liste over structs og jeg ønsker å endre ett element. For eksempel :

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

Nå ønsker jeg å endre ett element:

MyList[1].Name = bob

Men når jeg prøver og gjøre dette får jeg følgende feilmelding:

Kan ikke endre returverdien av System.Collections.Generic.List.this [int]' fordi det ikke er en variabel

Hvis jeg bruker en liste over klasser, ikke problemet oppstå.

Jeg tror svaret har å gjøre med structs være en verditype.

Så hvis jeg har en liste over structs skal jeg behandle dem som skrivebeskyttet ? Hvis jeg trenger å endre elementer i en liste så skal jeg bruke klasser og ikke structs?

Publisert på 09/09/2008 klokken 10:23
kilden bruker
På andre språk...                            


5 svar

stemmer
38

Ikke helt. Utforme en type som klasse eller struct bør ikke være drevet av behovet for å lagre den i samlingene :) Du bør se på 'semantikk' trengs

Problemet du ser er på grunn av verdien typen semantikk. Hver verdi type variabel / referanse er en ny forekomst. Når du sier

Struct obItem = MyList[1];

hva som skjer er at en ny instans av struct er opprettet og alle medlemmer er kopiert en etter en. Slik at du har en klone av myList [1] dvs 2 tilfeller. Nå hvis du endrer obItem, det påvirker ikke den opprinnelige.

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

Nå bærer med meg for 2 minutter her (Dette tar en stund å svelge ned .. det gjorde for meg :) Hvis du virkelig trenger structs å bli lagret i en samling og modifisert som du antydet i spørsmålet ditt, du må gjøre din struct utsette et grensesnitt ( Men dette vil resultere i boksing ). Man kan da modifisere selve struct via et grensesnitt referanse, som refererer til den eske objektet.

Følgende kodebit illustrerer hva jeg sa ovenfor

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. Godt spørsmål.
Oppdatering: @Hath - du fikk meg i gang for å sjekke om jeg har oversett noe så enkelt. (Det ville være inkonsekvent om setter-egenskaper ikke og metoder gjorde - Net universet er fortsatt balansert :)
Setter metoden fungerer ikke
obList2 [1] returnerer en kopi som stat ville bli endret. Original struct på listen forblir uendret. Så Set-via-grensesnitt synes å være eneste måten å gjøre det.

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);
}
Svarte 09/09/2008 kl. 10:56
kilden bruker

stemmer
29

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

structs i C # skal nesten alltid være utformet for å være uforanderlig (det vil si, har ingen mulighet til å endre sin interne tilstanden når de har blitt opprettet).

I ditt tilfelle, hva du ønsker å gjøre er å erstatte hele struct i spesifisert rekke indeksen, ikke forsøke å endre bare en enkelt eiendom eller feltet.

Svarte 09/09/2008 kl. 10:27
kilden bruker

stemmer
11

Det er ikke så mye at structs er "uforanderlige".

Den reelle underliggende problemet er at structs er en verdi type, ikke en referansetype. Så når du trekker ut en "referanse" til struct fra listen, er det å lage en ny kopi av hele struct. Så noen endringer du gjør på det endrer kopien, ikke den opprinnelige versjonen på listen.

Som Andrew sier, må du bytte hele struct. Som det punktet, men jeg tror du må spørre deg selv hvorfor du bruker en struct i første omgang (i stedet for en klasse). Pass på at du ikke gjør det rundt prematur optimalisering bekymringer.

Svarte 09/09/2008 kl. 22:22
kilden bruker

stemmer
4

Det er ingenting galt med structs som har eksponert felt, eller som gjør at mutasjon via eiendoms settere. Structs som mutere seg i respons til metoder eller eiendom getter, men er farlig fordi systemet vil tillate metoder eller eiendom getter for å bli kalt på midlertidige struct forekomster; hvis metoder eller getters gjøre endringer i struct, vil disse endringene ende opp med å bli forkastet.

Dessverre, som du nevner, samlinger bygget inn .net er veldig svak på å utsette verdi-type gjenstander som finnes der. Det beste alternativet er å gjøre noe sånt som:

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

Noe irriterende, og ikke i det hele tatt THREADSAFE. Fortsatt en forbedring over en liste over en klasse type, der gjør det samme kan kreve:

  myList [1] .name = "Albert";

men det kan også kreve:

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

eller kanskje

  MyClass temp = (MyClass) myList [1] .Clone ();
  temp.Name = "Albert";
  myList [1] = temp;

eller kanskje noen andre varianter. En virkelig ikke ville være i stand til å vite med mindre man undersøkt MyClass samt andre koder som sette ting på listen. Det er fullt mulig at man ikke kan være i stand til å vite om den første formen er trygt uten å undersøke kode i forsamlinger hvor man ikke har tilgang til. Derimot, hvis navn er en utsatt felt MyStruct, den metoden jeg ga for å oppdatere det vil fungere, uansett hva annet MyStruct inneholder, eller uavhengig av hva andre ting kan ha gjort med myList før koden utfører eller hva de kan forvente å gjøre med det etterpå.

Svarte 19/12/2011 kl. 07:59
kilden bruker

stemmer
0

I tillegg til de andre svarene, jeg trodde det kunne være nyttig å forklare hvorfor kompilatoren klager.

Når du ringer MyList[1].Name, i motsetning til en matrise, det MyList[1]faktisk kaller indekser metoden bak kulissene.

Hver gang en metoden returnerer en forekomst av en struct, du får en kopi av den struct (med mindre du bruker ref / ut).

Så du får en kopi og sette Nameeiendommen på en kopi, som er i ferd med å bli forkastet fordi kopien ikke ble lagret i en variabel hvor som helst.

Denne veiledningen beskriver hva som skjer i mer detalj (inkludert den genererte CIL kode).

Svarte 05/07/2019 kl. 05:39
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more