Una nueva función en C# /.NET 4.0 es que usted puede cambiar su enumerable en un foreach sin llegar a la excepción. Ver a Paul Jackson la entrada del blog de Un Efecto secundario Interesante de Simultaneidad: la Eliminación de Elementos de una Colección, Mientras que la Enumeración de para obtener información sobre este cambio.

¿Cuál es la mejor manera de hacer lo siguiente?

foreach(var item in Enumerable)
{
    foreach(var item2 in item.Enumerable)
    {
        item.Add(new item2)
    }
}

Por lo general el uso de un IList como una caché de búfer hasta el final de la foreach, pero hay una mejor manera?

  • Hmm.. ¿nos documentación de este cambio? Enumerables siempre han sido inmutable al enumerar más de la colección.
  • Esta es una variación sobre el tema que se le pida Steve McConnell para asesorar nunca mono con el índice de bucle.
  • Cambio en .NET 4.0 lovethedot.net/2009/03/…
  • Gracias por el enlace! Pero, me pregunto, se preguntan cómo hacer esto con o sin esta nueva funcionalidad? Supongo que sin el, ya que aceptó mi respuesta, pero estoy un poco confundido.
  • Sé que estoy cavando una vieja conversación aquí, pero me gustaría ser extremadamente cuidadoso con esto. Sólo las nuevas colecciones simultáneas son modificables dentro de un foreach – todos los anteriores tipos de colección, y me imagino que la mayoría de futuro de la colección de tipos demasiado, todavía va a ser inmutable, mientras que la enumeración sobre ellos. Haciendo amplio uso de este capricho efectivamente bloqueo en el uso de las colecciones simultáneas, porque si se quería utilizar una colección diferente en el futuro de todos los de su peculiar bucles foreach pronto iba a romper.
  • Posibles duplicados de en C# no de mono con el índice de bucle
  • Es esta pregunta específicamente sobre las colecciones simultáneas? O es pedirle a una pregunta más general y la mención de que sólo por contraste?
  • En el enlace de la pregunta ya no es válido. Hay una actualización de uno?

InformationsquelleAutor Polo | 2009-04-17

11 Comentarios

  1. 65

    La colección que se usa en el foreach es inmutable. Esto es en gran parte por el diseño.

    Como se dice en MSDN:

    La instrucción foreach es utilizado para
    iterar a través de la colección para obtener
    la información que desee, pero puede
    no se puede utilizar para agregar o quitar elementos
    de la colección de origen para evitar
    efectos colaterales impredecibles. Si
    necesidad de agregar o quitar elementos de la
    colección de origen, puede utilizar un bucle for.

    El post en el enlace proporcionada por Poko indica que esto está permitido en las nuevas colecciones simultáneas.

    • Estás respuesta no contesta la pregunta. Él sabe que la base de bucle foreach es inmutable… él quiere saber la mejor manera de aplicar cambios a una colección enumerable.
    • Entonces la respuesta es: el uso regular de bucle, como se sugiere en la cita. Soy consciente de que el OP menciona este comportamiento de los cambios en C# 4.0, pero no puedo encontrar ninguna información sobre eso. Por ahora, creo que esta es todavía una respuesta pertinente.
    • lovethedot.net/2009/03/…
  2. 15

    Hacer una copia de la enumeración, el uso de un IEnumerable método de extensión en este caso, y enumerar sobre él. Esto habría que agregar una copia de cada elemento en cada interior enumerable a esa enumeración.

    foreach(var item in Enumerable)
    {
        foreach(var item2 in item.Enumerable.ToList())
        {
            item.Add(item2)
        }
    }
    • Pero ¿por qué copiar el enumerable de una lista en lugar de un array? Una lista proporciona métodos para buscar, ordenar y manipular la colección, en la que no necesitamos.
  3. 8

    Como se ha mencionado, pero con un ejemplo de código:

    foreach(var item in collection.ToArray())
        collection.Add(new Item...);
    • Esto no es una gran respuesta, porque realiza innecesarios y asignaciones de copias (ToArray()) sólo para iterar sobre la lista.
  4. 7

    Para ilustrar Nippysaurus la respuesta: Si usted va a agregar los nuevos elementos a la lista y desea procesar los elementos recién agregados también durante la misma enumeración que sólo puede utilizar para bucle en lugar de foreach bucle, problema resuelto 🙂

    var list = new List<YourData>();
    ... populate the list ...
    
    //foreach (var entryToProcess in list)
    for (int i = 0; i < list.Count; i++)
    {
        var entryToProcess = list[i];
    
        var resultOfProcessing = DoStuffToEntry(entryToProcess);
    
        if (... condition ...)
            list.Add(new YourData(...));
    }

    Para ejecutables ejemplo:

    void Main()
    {
        var list = new List<int>();
        for (int i = 0; i < 10; i++)
            list.Add(i);
    
        //foreach (var entry in list)
        for (int i = 0; i < list.Count; i++)
        {
            var entry = list[i];
            if (entry % 2 == 0)
                list.Add(entry + 1);
    
            Console.Write(entry + ", ");
        }
    
        Console.Write(list);
    }

    Salida de último ejemplo:

    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3, 5, 7, 9,

    Lista (15 artículos)

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    1

    3

    5

    7

    9

  5. 4

    He aquí cómo usted puede hacer que (quick and dirty solución. Si usted realmente necesidad de este tipo de comportamiento, debe reconsiderar su diseño o reemplazar todos los IList<T> miembros y el agregado de la lista de origen):

    using System;
    using System.Collections.Generic;
    
    namespace ConsoleApplication3
    {
        public class ModifiableList<T> : List<T>
        {
            private readonly IList<T> pendingAdditions = new List<T>();
            private int activeEnumerators = 0;
    
            public ModifiableList(IEnumerable<T> collection) : base(collection)
            {
            }
    
            public ModifiableList()
            {
            }
    
            public new void Add(T t)
            {
                if(activeEnumerators == 0)
                    base.Add(t);
                else
                    pendingAdditions.Add(t);
            }
    
            public new IEnumerator<T> GetEnumerator()
            {
                ++activeEnumerators;
    
                foreach(T t in ((IList<T>)this))
                    yield return t;
    
                --activeEnumerators;
    
                AddRange(pendingAdditions);
                pendingAdditions.Clear();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                ModifiableList<int> ints = new ModifiableList<int>(new int[] { 2, 4, 6, 8 });
    
                foreach(int i in ints)
                    ints.Add(i * 2);
    
                foreach(int i in ints)
                    Console.WriteLine(i * 2);
            }
        }
    }
  6. 2

    LINQ es muy eficaz para hacer malabares con las colecciones.

    Sus tipos y la estructura está claro para mí, pero voy a tratar de encajar su ejemplo a la mejor de mi capacidad.

    De su código parece que, para cada elemento, se agrega la opción de todo, desde su propia » Enumerable de la propiedad. Esto es muy simple:

    foreach (var item in Enumerable)
    {
        item = item.AddRange(item.Enumerable));
    }

    Como un ejemplo más general, digamos que queremos recorrer una colección y quitar elementos en los que una determinada condición es true. Evitar foreach, utilizando LINQ:

    myCollection = myCollection.Where(item => item.ShouldBeKept);

    Agregar un elemento basado en cada elemento existente? No hay problema:

    myCollection = myCollection.Concat(myCollection.Select(item => new Item(item.SomeProp)));
  7. 1

    No se puede cambiar el enumerable de la colección mientras se enumeran, por lo que tendrá que hacer los cambios antes o después de enumerar.

    La for bucle es una buena alternativa, pero si su IEnumerable colección no implementar ICollection, no es posible.

    Ya sea:

    1) Copia de la colección de primera. Enumerar el copiado de la colección y cambiar el original de la colección durante la enumeración. (@tvanfosson)

    o

    2) Mantener una lista de cambios y confirmar después de la enumeración.

  8. 1

    El mejor enfoque desde una perspectiva de rendimiento es, probablemente, el uso de uno o de dos matrices. Copia de la lista a una matriz, hacer operaciones sobre la matriz y, a continuación, crear una nueva lista de la matriz. El acceso a un elemento de la matriz es más rápido que el acceso a un elemento de la lista, y las conversiones entre un List<T> y un T[] puede utilizar un rápido «de copia masiva» de la operación que evita la sobrecarga asociada acceder a los elementos individuales.

    Por ejemplo, suponga que tiene un List<string> y el deseo de tener cada cadena en la lista que comienza con T ser seguido por un elemento de «Boo», mientras que la cadena que comienza con la «U» se ha caído por completo. Un enfoque óptimo probablemente sería algo como:

    int srcPtr,destPtr;
    string[] arr;
    
    srcPtr = theList.Count;
    arr = new string[srcPtr*2];
    theList.CopyTo(arr, theList.Count); //Copy into second half of the array
    destPtr = 0;
    for (; srcPtr < arr.Length; srcPtr++)
    {
      string st = arr[srcPtr];
      char ch = (st ?? "!")[0]; //Get first character of string, or "!" if empty
      if (ch != 'U')
        arr[destPtr++] = st;
      if (ch == 'T')
        arr[destPtr++] = "Boo";
    }
    if (destPtr > arr.Length/2) //More than half of dest. array is used
    {
      theList = new List<String>(arr); //Adds extra elements
      if (destPtr != arr.Length)
        theList.RemoveRange(destPtr, arr.Length-destPtr); //Chop to proper length
    }
    else
    {
      Array.Resize(ref arr, destPtr);
      theList = new List<String>(arr); //Adds extra elements
    }

    Hubiera sido útil si List<T> proporcionado un método para elaborar una lista de una parte de un array, pero no me he enterado de cualquier método eficiente para hacerlo. Aún así, las operaciones sobre matrices son bastante rápidos. Es de destacar el hecho de que la adición y eliminación de elementos de la lista no requiere de «empujar» en torno a otros elementos; cada elemento se escribe directamente a su lugar apropiado en la matriz.

  9. 0

    Realmente debería usar for() en lugar de foreach() en este caso.

    • Que potencialmente desorden código con uno de los más variable que contendrá el número original de los elementos de la colección.
    • Si se cambia la longitud de la colección, entonces usted quiere que su bucle for para ser evaluado en contra de la nueva longitud, no el original o usted podría conseguir un fuera de rango excepción
    • Usted será necesita que la variable adicional, porque usted tiene que manejar la iteración cuando la colección se modifica
    • Suena muy bien, y estoy de acuerdo con usted en la teoría, pero algunas colecciones no pueden ser indexados. Usted tiene que iterar sobre ellos.
  10. 0

    Para agregar a Timo de la respuesta de la LINQ puede ser utilizado como esta así:

    items = items.Select(i => {
    
         ...
         //perform some logic adding /updating.
    
         return i / return new Item();
         ...
    
         //To remove an item simply have logic to return null.
    
         //Then attach the Where to filter out nulls
    
         return null;
         ...
    
    
    }).Where(i => i != null);
  11. 0

    He escrito un solo paso sencillo, pero debido a esto se reduce el rendimiento

    Aquí es mi fragmento de código:-

    for (int tempReg = 0; tempReg < reg.Matches(lines).Count; tempReg++)
                                {
                                    foreach (Match match in reg.Matches(lines))
                                    {
                                        var aStringBuilder = new StringBuilder(lines);
                                        aStringBuilder.Insert(startIndex, match.ToString().Replace(",", " ");
                                        lines[k] = aStringBuilder.ToString();
                                        tempReg = 0;
                                        break;
                                    }
                                }

Dejar respuesta

Please enter your comment!
Please enter your name here