C# 如何在使用集合时将项目添加到集合中?

C# 如何在使用集合时将项目添加到集合中?,c#,.net,C#,.net,下面的示例在执行代码时抛出InvalidOperationException,“Collection已修改;枚举操作可能无法执行” 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)

下面的示例在执行代码时抛出InvalidOperationException,“Collection已修改;枚举操作可能无法执行”

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
}
var url=newlist();
URL.Add(“http://www.google.com");
foreach(url中的字符串url)
{
//从url获取所有链接
List newurl=GetLinks(url);

AddRange(newUrls);//我将创建两个列表添加到第二个列表中,然后像这样更新引用:

var urls = new List<string>();
var destUrls = new List<string>(urls);
urls.Add("http://www.google.com");
foreach (string url in urls)
{    
    // Get all links from the url    
    List<string> newUrls = GetLinks(url);    
    destUrls.AddRange(newUrls);
}
urls = destUrls;
var url=newlist();
var destUrls=新列表(URL);
URL.Add(“http://www.google.com");
foreach(url中的字符串url)
{    
//从url获取所有链接
List newurl=GetLinks(url);
destUrls.AddRange(newUrls);
}
URL=destURL;

或者,您可以将集合视为队列

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);
}
IList URL=new List();
URL.Add(“http://www.google.com");
而(url.Count>0)
{
字符串url=url[0];
url.RemoveAt(0);
//从url获取所有链接
List newurl=GetLinks(url);
AddRange(newURL);
}

不要更改正在为每个循环通过的集合。只需在列表的Count属性上使用while循环,并按索引访问列表项。这样,即使添加了项,迭代也应该获得更改

编辑:再次强调,这在某种程度上取决于您是否希望循环拾取您添加的新项目。如果不希望,则这将没有帮助

编辑2:我想最简单的方法是将循环更改为: foreach(url.ToArray()中的字符串url)


这将为您的列表创建一个数组副本,它将在此列表而不是原始列表中循环。这将产生不在添加的项目上循环的效果。

考虑使用带有while循环的队列(while q.Count>0,url=q.Dequeue())而不是迭代。

基本上,您不能这样做。您真正想要的是一个队列:

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

while(urls.Count != 0)
{
    String url = url.Dequeue();
    // Get all links from the url
    List<string> newUrls = GetLinks(url);
    foreach (string newUrl in newUrls)
    {
        queue.Enqueue(newUrl);
    }
}
var url=new Queue();
URL.Enqueue(“http://www.google.com");
while(url.Count!=0)
{
字符串url=url.Dequeue();
//从url获取所有链接
List newurl=GetLinks(url);
foreach(newUrl中的字符串newUrl)
{
queue.Enqueue(newUrl);
}
}

这有点难看,因为
队列中没有
AddRange
方法,但我认为这基本上就是你想要的。

将foreach与lambda一起使用,它更有趣

var urls = new List<string>();
var destUrls = new List<string>();
urls.Add("http://www.google.com");
urls.ForEach(i => destUrls.Add(GetLinks(i)));
urls.AddRange(destUrls);
var url=newlist();
var destUrls=新列表();
URL.Add(“http://www.google.com");
ForEach(i=>destUrls.Add(GetLinks(i));
AddRange(destURL);

我假设您希望迭代整个列表,以及添加到其中的每个项目?如果是这样,我建议递归:

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

iterate(turls);

function iterate(List<string> u)
{
    foreach(string url in u)
    {
        List<string> newUrls = GetLinks(url);

        urls.AddRange(newUrls);

        iterate(newUrls);
    }
}
var url=newlist();

var turls=newlist您可以使用三种策略

  • 将列表复制到第二个集合(列表或数组-可能使用ToArray())。循环第二个集合,向第一个集合添加URL
  • 创建第二个列表,在URL列表中循环,向第二个列表中添加新值。循环完成后,将这些值复制到原始列表中
  • 使用for循环而不是foreach循环。将计数放在前面。列表应正确索引内容,以便在添加内容时,它们将转到列表的末尾
  • 我更喜欢#3,因为它没有与#1或#2相关的任何开销。下面是一个示例:

    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);
    }
    
    var url=newlist();
    URL.Add(“http://www.google.com");
    int count=url.count;
    for(int index=0;index


    编辑:最后一个示例(#3)假设您不想在循环中找到其他URL时处理这些URL。如果您确实想在找到其他URL时处理这些URL,只需使用URL。在for循环中计数,而不是此答案注释中提到的本地计数变量。

    您可能还可以创建一个递归ve函数,如下所示(未测试):

    IEnumerable GetUrl(字符串url)
    {
    foreach(GetUrl(url)中的字符串u)
    收益率;
    foreach(WHERE\u I\u GET\u MY\u url中的字符串ret\u url)
    收益回报率;
    }
    列表MyEnumerateFunction()
    {
    返回新列表(GetUrl(“http://www.google.com"));
    }
    
    在这种情况下,您将不必创建两个列表,因为GetUrl完成了所有工作


    但是我可能没有抓住你程序的要点。

    Jon的方法是正确的;队列是这种应用程序的正确数据结构

    假设您最终希望您的程序终止,我建议您做两件事:

    • 不要对URL使用
      string
      ,请使用
      System.Web.Uri
      :它提供URL的规范字符串表示形式。这对于第二个建议很有用,即
    • 将处理的每个URL的规范字符串表示形式放入字典中。在将URL排队之前,请先检查它是否在字典中

      • 如果不知道GetLinks()是什么,就很难让代码变得更好是的。无论如何,这避免了递归。标准习惯用法是在枚举集合时不更改集合。虽然运行时可以允许您这样做,但原因是它是错误源,因此最好创建新集合或自己控制迭代

      • 创建包含所有URL的队列
      • 当退出队列时,我们几乎是在说我们已经处理了它,所以将它添加到结果中
      • 如果GetLinks()返回任何内容,请将它们添加到队列中并进行处理
      • *正如其他人指出的,使用字典可能更有效,这取决于您处理的项目数量


        我觉得奇怪的是GetLinks()会返回一个值,然后再将其解析为更多的Url。也许您只需要进行一级扩展。如果是这样,我们就可以完全摆脱队列

        public static List<string> StraightProcess(List<string> urls)
        {
            List<string> result = new List<string>();
        
            foreach (string url in urls)
            {
                result.Add(url);
                result.AddRange(GetLinks(url));
            }
        
            return result;
        }
        
        公共静态列表进程(列表URL)
        {
        锂
        
        public List<string> ExpandLinksOrSomething(List<string> urls)
        {
            List<string> result = new List<string>();
            Queue<string> queue = new Queue<string>(urls);
        
            while (queue.Any())
            {
                string url = queue.Dequeue();
                result.Add(url);
        
                foreach( string newResult in GetLinks(url) )
                {
                    queue.Enqueue(newResult);
                }
        
            }
        
            return result;
        }
        
                List<string> newItems = GetLinks(url).Except(result).ToList();
                foreach( string newResult in newItems )
                {
                    queue.Enqueue(newResult);
                }
        
        public static List<string> StraightProcess(List<string> urls)
        {
            List<string> result = new List<string>();
        
            foreach (string url in urls)
            {
                result.Add(url);
                result.AddRange(GetLinks(url));
            }
        
            return result;
        }