Perché l'impostazione di una proprietà su un oggetto elencato non funziona?

StackOverflow https://stackoverflow.com/questions/317527

  •  11-07-2019
  •  | 
  •  

Domanda

So molto di C # ma questo mi sta sconcertando e Google non mi aiuta.

Ho una gamma di oggetti IEnumerable. Voglio impostare una proprietà sul primo. Lo faccio, ma quando enumero la gamma di oggetti dopo la modifica, non vedo la mia modifica.

Ecco un buon esempio del problema:

    public static void GenericCollectionModifier()
    {
        // 1, 2, 3, 4... 10
        var range = Enumerable.Range(1, 10);

        // Convert range into SubItem classes
        var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

        Write(items);  // Expect to output 1,2,3,4,5,6,7,8,9,10

        // Make a change
        items.First().MagicNumber = 42;

        Write(items);  // Expect to output 42,2,3,4,5,6,7,8,9,10
        // Actual output: 1,2,3,4,5,6,7,8,9,10
    }

    public static void Write(IEnumerable<SubItem> items)
    {
        Console.WriteLine(string.Join(", ", items.Select(item => item.MagicNumber.ToString()).ToArray()));
    }

    public class SubItem
    {
       public string Name;
       public int MagicNumber;
    }

Quale aspetto di C # ferma il mio " MagicNumber = 42 " cambiare dall'output? C'è un modo per ottenere la mia modifica in & Quot; stick & Quot; senza fare qualche conversione funky in List < > o array?

Grazie! -Mike

È stato utile?

Soluzione

Quando chiami First () viene enumerato il risultato di questo bit di codice:

Select(i => new SubItem() {Name = "foo", MagicNumber = i});

Nota che Select è un enumeratore pigro, il che significa che esegue la selezione solo quando chiedi un elemento da esso (e lo fa ogni ogni volta che lo chiedi). I risultati non vengono archiviati da nessuna parte, quindi quando si chiamano items.First () si ottiene una nuova SubItem istanza. Quando poi passi elementi a Write, si ottiene un sacco di nuove <=> istanze - non quella che hai prima.

Se vuoi memorizzare il risultato della tua selezione e modificarlo, devi fare qualcosa del tipo:

var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();

Altri suggerimenti

Sospetto che qualcosa in background. Molto probabilmente a causa del fatto che IEnumerables può essere ripetuto una sola volta.

Funziona se aggiungi un 'ToList ()' dopo la chiamata a Select () quando assegni a 'items'?

L'unica cosa che mi viene in mente è che items.First () passa una copia di SubItem al tuo anziché al riferimento, quindi quando lo imposti la modifica non viene effettuata.

Devo presumere che abbia a che fare con IQueryable potendo essere iterato una sola volta. Puoi provare a cambiare questo:

// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

a

// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();

E vedi se ci sono risultati diversi.

Non puoi / non dovresti modificare una raccolta tramite un enumeratore. Sono sorpreso che questo non generi un'eccezione.

.First () è un metodo, non una proprietà. Restituisce una nuova istanza dell'oggetto nella prima posizione dell'Enumerable.

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