Pergunta

Um novo recurso do C # / .NET 4.0 é que você pode mudar a sua enumerável em um foreach sem obter a exceção. blog See de Paul Jackson um interessante efeito colateral da Concorrência: remover itens de uma coleção ao enumerar para obter informações sobre esta mudança

.

O que é a melhor maneira de fazer o seguinte?

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

Normalmente, eu uso um IList como um cache / buffer até o fim do foreach, mas há melhor maneira?

Foi útil?

Solução

A coleção usado em foreach é imutável. Isto é muito pelo design.

Como se diz na MSDN :

A instrução foreach é usado para iterar na coleção para obter a informação que você quer, mas lata não pode ser usado para adicionar ou itens remove da coleção de origem para evitar efeitos colaterais imprevisíveis. Se você precisa adicionar ou remover itens da coleção de origem, use um loop for.

O post no href="http://www.lovethedot.net/2009/03/interesting-side-effect-of-concurrency.html" rel="noreferrer"> ligação forneceu por Poko indica que isso é permitido nas novas coleções simultâneas.

Outras dicas

Faça uma cópia da enumeração, utilizando um método de extensão IEnumerable neste caso, e enumerar sobre ele. Isto acrescentaria uma cópia de cada elemento em cada enumeráveis ??interior para que a enumeração.

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

Como mencionado, mas com uma amostra de código:

foreach(var item in collection.ToArray())
    collection.Add(new Item...);

Para ilustrar a resposta de Nippysaurus: Se você estiver indo para Adicionar os novos itens à lista e quer processar os itens adicionados recentemente também durante o mesmo enumeração, em seguida, você pode simplesmente usar para circuito em vez de foreach loop, problema resolvido:)

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

Por exemplo executável:

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

Saída do último exemplo:

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

List (15 ítens)
0
1 Página 2 Sims 3 página 4
5
6
7
8
9
1 Sims 3
5
7
9

Veja como você pode fazer isso (solução rápida e suja Se você realmente precisa deste tipo de comportamento, você deve reconsiderar seu projeto ou substituir todos os membros IList<T> e agregar a lista de origem.):

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

LINQ é muito eficaz para malabarismo com coleções.

Seus tipos e estrutura não são claras para mim, mas vou tentar encaixar o seu exemplo para o melhor de minha capacidade.

A partir do seu código, parece que, para cada item, você está adicionando ao que tudo item de sua própria propriedade 'Enumerable'. Isto é muito simples:

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

Como um exemplo mais geral, vamos dizer que queremos iterar uma coleção e remover itens em que uma determinada condição é verdadeira. Evitando foreach, usando LINQ:

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

Adicionar um item com base em cada item existente? Não há problema:

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

Você não pode alterar a coleção enumerável enquanto ele está sendo enumerados, assim você terá que fazer as alterações antes ou depois de enumerar.

O loop for é uma boa alternativa, mas se sua coleção IEnumerable não implementa ICollection, não é possível.

Ou:

1) recolha de cópia primeiro. Enumerar a coleção copiado e alterar a coleção original durante a enumeração. (@Tvanfosson)

ou

2) Manter uma lista de mudanças e comprometer-los após a enumeração.

A melhor abordagem a partir de uma perspectiva de desempenho é provavelmente usar uma ou duas matrizes. Copiar a lista para uma matriz, fazer operações na matriz, e então construir uma nova lista a partir da matriz. Acessando um elemento de matriz é mais rápido do que acessar um item da lista, e as conversões entre a List<T> e uma T[] pode usar uma operação rápida "cópia em massa" que evita a sobrecarga associada acessar itens individuais.

Por exemplo, suponha que você tenha um List<string> e desejo de ter cada corda na lista que começa com T ser seguido por um item "Boo", enquanto que cada cadeia que começa com "U" é completamente descartada. Uma abordagem melhor seria provavelmente 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
}

Ela teria sido útil se List<T> fornecido um método para a construção de uma lista a partir de uma porção de uma matriz, mas estou desconhece qualquer método eficiente para fazê-lo. Ainda assim, as operações sobre matrizes são muito rápidos. Digno de nota é o fato de que adicionar e remover itens da lista não requer "empurrar" em torno de outros itens; cada item é escrita diretamente ao seu local apropriado na matriz.

Você realmente deve usar for() vez de foreach() neste caso.

Para adicionar a resposta LINQ de Timo pode ser usado como este, bem como:

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

Eu escrevi uma etapa fácil, mas por causa deste desempenho será degradado

Aqui está o meu trecho 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;
                                }
                            }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top