c# ¿Cómo agregar elementos a una colección mientras la consume?




5 Answers

Hay tres estrategias que puedes usar.

  1. Copie la Lista <> a una segunda colección (lista o matriz - quizás use ToArray ()). Pasa por esa segunda colección, añadiendo URLs a la primera.
  2. Cree una segunda lista <> y recorra su lista de urls <> agregando nuevos valores a la segunda lista. Copie esos a la lista original cuando termine de buclear.
  3. Use un bucle for en lugar de un bucle foreach . Toma tu conteo por adelantado. La lista debe dejar las cosas indexadas correctamente, por lo que debe agregar cosas que irán al final de la lista.

Prefiero # 3 ya que no tiene ninguno de los gastos generales asociados con # 1 o # 2. Aquí hay un ejemplo:

var urls = new List<string>();
urls.Add("http://www.google.com");
int count = urls.Count;

for (int index = 0; index < count; index++)
{
    // Get all links from the url
    List<string> newUrls = GetLinks(urls[index]);

    urls.AddRange(newUrls);
}

Editar: el último ejemplo (n. ° 3) asume que no desea procesar URL adicionales ya que se encuentran en el ciclo. Si desea procesar URL adicionales a medida que se encuentran, solo use urls.Count en el ciclo for en lugar de la variable de conteo local mencionada por el configurador en los comentarios para esta respuesta.

c# .net

El siguiente ejemplo arroja InvalidOperationException, "La colección fue modificada, la operación de enumeración puede no ejecutarse". al ejecutar el código.

var urls = new List<string>();
urls.Add("http://www.google.com");

foreach (string url in urls)
{
    // Get all links from the url
    List<string> newUrls = GetLinks(url);

    urls.AddRange(newUrls); // <-- This is really the problematic row, adding values to the collection I'm looping
}

¿Cómo puedo reescribir esto de una mejor manera? Supongo que una solución recursiva?




alternativamente, podría tratar la colección como una cola

IList<string> urls = new List<string>();
urls.Add("http://www.google.com");
while (urls.Count > 0)
{
    string url = urls[0];
    urls.RemoveAt(0);
    // Get all links from the url
    List<string> newUrls = GetLinks(url);
    urls.AddRange(newUrls);
}



Considere usar una cola con un ciclo while (mientras q.Count> 0, url = q.Dequeue ()) en lugar de iteración.




Probablemente también pueda crear una función recursiva, como esta (no probada):

IEnumerable<string> GetUrl(string url)
{
  foreach(string u in GetUrl(url))
    yield return u;
  foreach(string ret_url in WHERE_I_GET_MY_URLS)
    yield return ret_url;
}

List<string> MyEnumerateFunction()
{
  return new List<string>(GetUrl("http://www.google.com"));
}

En este caso, no tendrá que crear dos listas, ya que GetUrl hace todo el trabajo.

Pero puede haber perdido el punto de su programa.




El enfoque de Jon es correcto; una cola es la estructura de datos correcta para este tipo de aplicación.

Suponiendo que finalmente le gustaría que su programa termine, sugeriría otras dos cosas:

  • no use string para sus URL, use System.Web.Uri : proporciona una representación de cadena canónica de la URL. Esto será útil para la segunda sugerencia, que es ...
  • ponga la representación de cadena canónica de cada URL que procesa en un diccionario. Antes de enrutar una URL, verifique primero si está en el diccionario.



Related


Tags

c#   .net