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