Domanda

Ho un elenco di strutture e voglio modificare un elemento.Per esempio :

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

Ora voglio cambiare un elemento:

MyList[1].Name = "bob"

Tuttavia, ogni volta che provo a farlo, ottengo il seguente errore:

Impossibile modificare il valore di ritorno di System.Collections.generic.list.this [int] 'perché non è una variabile

Se utilizzo un elenco di classi, il problema non si verifica.

Immagino che la risposta abbia a che fare con le strutture che sono un tipo di valore.

Quindi, se ho un elenco di strutture, dovrei trattarle come sola lettura?Se devo modificare gli elementi in un elenco, dovrei utilizzare le classi e non le strutture?

È stato utile?

Soluzione

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

le strutture in C# dovrebbero quasi sempre essere progettate per essere immutabili (ovvero, non avere modo di modificare il proprio stato interno una volta create).

Nel tuo caso, ciò che vuoi fare è sostituire l'intera struttura nell'indice dell'array specificato, non provare a modificare solo una singola proprietà o campo.

Altri suggerimenti

Non proprio.Progettare un tipo come classe o struttura non dovrebbe essere guidato dalla necessità di memorizzarlo in raccolte :) Dovresti considerare la "semantica" necessaria

Il problema che stai riscontrando è dovuto alla semantica del tipo di valore.Ogni variabile/riferimento di tipo valore è una nuova istanza.Quando dici

Struct obItem = MyList[1];

ciò che accade è che viene creata una nuova istanza della struttura e tutti i membri vengono copiati uno per uno.In modo da avere un clone di MyList[1], ad es.2 istanze.Ora se modifichi obItem, ciò non influisce sull'originale.

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

Ora abbi pazienza per 2 minuti qui (ci vuole un po' per mandarlo giù...È stato per me :) Se hai davvero bisogno di strutture per essere archiviate in una raccolta e modificate come indicato nella tua domanda, dovrai fare in modo che la tua struttura esponga un'interfaccia (Tuttavia ciò si tradurrà nella boxe).È quindi possibile modificare la struttura effettiva tramite un riferimento all'interfaccia, che fa riferimento all'oggetto boxed.

Il seguente frammento di codice illustra ciò che ho appena detto sopra

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.Buona domanda.
Aggiornamento: @Hath: mi hai fatto correre per verificare se avevo trascurato qualcosa di così semplice.(Sarebbe incoerente se le proprietà del setter e i metodi non lo facessero: l'universo .Net è ancora bilanciato :)
Il metodo setter non funziona
obList2[1] restituisce una copia il cui stato verrebbe modificato.La struttura originale nell'elenco rimane invariata.Quindi Set-via-Interface sembra essere l'unico modo per farlo.

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);
}

Non è tanto il fatto che le strutture siano "immutabili".

Il vero problema di fondo è che le strutture sono un tipo Valore, non un tipo Riferimento.Pertanto, quando estrai un "riferimento" alla struttura dall'elenco, viene creata una nuova copia dell'intera struttura.Pertanto, qualsiasi modifica apportata modifica la copia, non la versione originale nell'elenco.

Come afferma Andrew, devi sostituire l'intera struttura.A quel punto, però, penso che dovresti chiederti perché stai usando una struttura in primo luogo (invece di una classe).Assicurati di non farlo per problemi di ottimizzazione prematura.

Non c'è niente di sbagliato nelle strutture che hanno campi esposti o che consentono la mutazione tramite setter di proprietà.Le strutture che mutano in risposta a metodi o getter di proprietà, tuttavia, sono pericolose perché il sistema consentirà la chiamata di metodi o getter di proprietà su istanze temporanee di struct;se i metodi o i getter apportano modifiche alla struttura, tali modifiche finiranno per essere scartate.

Sfortunatamente, come hai notato, le raccolte integrate in .net sono davvero deboli nell'esporre gli oggetti di tipo valore in esse contenuti.La soluzione migliore è solitamente fare qualcosa del tipo:

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

Un po' fastidioso e per nulla sicuro.Ancora un miglioramento rispetto a un elenco di un tipo di classe, in cui fare la stessa cosa potrebbe richiedere:

  myList[1].Name = "Albert";

ma potrebbe anche richiedere:

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

o forse

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

o forse qualche altra variazione.Non si sarebbe davvero in grado di saperlo a meno che non si esaminasse myClass e l'altro codice che inserisce le cose nell'elenco.È del tutto possibile che non si possa sapere se il primo modulo è sicuro senza esaminare il codice negli assembly a cui non si ha accesso.Al contrario, se Name è un campo esposto di MyStruct, il metodo che ho fornito per aggiornarlo funzionerà, indipendentemente da cos'altro contiene MyStruct o indipendentemente da cosa altre cose potrebbero aver fatto con myList prima che il codice venga eseguito o cosa potrebbero aspettarsi di fare farlo dopo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top