Pourquoi la définition d'une propriété sur un objet énuméré ne fonctionne-t-elle pas?

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

  •  11-07-2019
  •  | 
  •  

Question

Je connais beaucoup de choses sur C #, mais celui-ci me bloque et Google ne m'aide pas.

J'ai une gamme d'objets IEnumerable. Je veux définir une propriété sur la première. Je le fais, mais lorsque j'énumère la gamme d'objets après la modification, je ne vois pas mon changement.

Voici un bon exemple du problème:

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

Quel aspect de C # arrête mon " MagicNumber = 42 " changer d'être en sortie? Existe-t-il un moyen d'obtenir mon changement en & "Coller &"; sans faire de la conversion géniale en liste < > ou tableau?

Merci! -Mike

Était-ce utile?

La solution

Lorsque vous appelez First (), il énumère le résultat de ce bit de code:

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

Notez que la sélection est un énumérateur paresseux, ce qui signifie qu'elle ne fait la sélection que lorsque vous lui demandez un élément (et le fait toutes les fois que vous le lui demandez). Les résultats ne sont stockés nulle part. Par conséquent, lorsque vous appelez items.First (), vous obtenez une nouvelle instance SubItem. Lorsque vous transmettez ensuite des éléments à Write, une multitude de nouvelles <=> instances - et non celle que vous aviez auparavant - est générée.

Si vous souhaitez stocker le résultat de votre sélection et le modifier, vous devez procéder comme suit:

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

Autres conseils

Je soupçonne que quelque chose se passe en arrière-plan. Probablement parce que IEnumerables ne peut être itéré qu'une seule fois.

Cela fonctionne-t-il si vous ajoutez un 'ToList ()' après l'appel à Select () lors de l'attribution à des 'éléments'?

La seule chose à laquelle je peux penser est que items.First () passe une copie de SubItem à votre place, plutôt qu'à la référence. Ainsi, lorsque vous le définissez, la modification n'est pas effectuée.

Je dois supposer que cela a quelque chose à voir avec IQueryable ne pouvant être itéré qu'une seule fois. Vous voudrez peut-être essayer de changer ceci:

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

à

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

Et voyez s'il y a des résultats différents.

Vous ne pouvez pas / ne devriez pas modifier une collection par le biais d'un énumérateur. Je suis surpris que cela ne jette pas une exception.

.First () est une méthode, pas une propriété. Il retourne une nouvelle instance de l'objet à la première position de votre Enumerable.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top