我有一本字典,如果字典中的项目通过了我要删除它的所有处理。
var dictEnum = dictObj.GetEnumerator();
while (dictEnum.MoveNext())
{
Parallel.ForEach(dictObj, pOpt, (KVP, loopState) =>
{
processAndRemove(KVP.Key);
});
}
private void processAndRemove(string keyId)
{
try
{
<does stuff>
dictObj.Remove(keyId);
} catch(exception ex) {
...
<does not remove anything, wants to retry until it doesn't fail>
}
}
我希望循环继续处理字典中的所有剩余项目(未删除)。
但是,我收到了一个错误。当我运行此代码的更简单版本时,我收到一条消息说明:
收藏被修改;枚举操作可能无法执行
有没有办法用字典做到这一点?
更新
只是为了提供更多背景信息。这背后的想法是如果dictObj中还有项目,循环继续运行。因此,如果我从10和8开始,我想重新运行直到它们没有通过的2。
答案 0 :(得分:4)
正如Jalayn所说,你在列举时不能从馆藏中删除。您必须重写代码,以便将其添加到另一个集合中,然后枚举该集合并删除原始集合中的项目。
类似的东西:
var toRemove = new Dictionary<int, string>() //whatever type it is
Parallel.ForEach(dictObj, pOpt, (KVP, loopState) =>
{
toRemove.Add(KVP);
});
foreach (var item in toRemove)
{
dictObject.Remove(item.Key);
}
答案 1 :(得分:3)
如果您在同一时间迭代它,则无法从集合中删除该项。 但是,您可以将要删除的所有元素存储在单独的集合中。
然后,当您完成枚举后,您可以遍历列表以从原始集合中删除每个项目。
或者,查看Best way to remove multiple items matching a predicate from a c# Dictionary?。这很美。用户@JaredPar提供的接受的答案摘录是:
foreach ( var s in MyCollection.Where(p => p.Value.Member == foo).ToList() ) {
MyCollection.Remove(s.Key);
}
答案 2 :(得分:1)
我认为你不能用字典做到这一点。相反,您可以执行Dictionary.Values.ToList()
之类的操作,删除您想要的内容,然后协调差异。
此问题有关于它的更多信息Collection was modified; enumeration operation may not execute
答案 3 :(得分:1)
启动第二个集合,并在其中添加要保留的值。
答案 4 :(得分:1)
为什么要明确调用GetEnumerator()
而不是foreach
? foreach
声明可以帮助您。在这种情况下,您在循环中使用MoveNext()
,但您从未阅读过Current
属性。
您似乎尝试在Parallel.ForEach
上使用dictObj
,但是您确定它是一种线程安全的类型吗?可能不是。它的类型是什么?
最后,错误文本说明了一切。您无法修改正在迭代的同一个集合。
答案 5 :(得分:1)
从我的谈话中:Jeppe Stig Nielsen产生了一个尝试ConcurrentDictionary
这是我的测试代码,我可以从Dictionary中删除项目(在Parallel.Foreach循环中),while
循环继续,直到count == 0 or the retryAttempts > 5
public static ConcurrentDictionary<string, myRule> ccDict= new ConcurrentDictionary<string, myRule>();
try
{
while (ccDict.Count > 0)
{
Parallel.ForEach(ccDict, pOptions, (KVP, loopState) =>
{
//This is the flag that tells the loop do exit out of loop if a cancellation has been requested
pOptions.CancellationToken.ThrowIfCancellationRequested();
processRule(KVP.Key, KVP.Value, loopState);
}); //End of Parallel.ForEach loop
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
Console.ReadLine();
}
public static int processRule(string rId, myRule rule, ParallelLoopState loopState)
{
try
{
if (rId == "001" || rId == "002")
{
if (rId == "001" && ccDict[rId].RetryAttempts == 2)
{
operationPassed(rId);
return 0;
}
operationFailed(rId);
}
else
{
operationPassed(rId);
}
return 0;
}
catch (Exception ex)
{
Console.WriteLine("failed : " + ex.Message.ToString());
return -99;
}
}
private static void operationPassed(string rId)
{
//Normal Operation
ccDict[rId].RulePassed = true;
ccDict[rId].ExceptionMessage = "";
ccDict[rId].ReturnCode = 0;
Console.WriteLine("passed: " + rId + " Retry Attempts : " + ccDict[rId].RetryAttempts.ToString());
rule value;
ccDict.TryRemove(rId, out value);
}
private static void operationFailed(string ruleId)
{
//This acts as if an EXCEPTION has OCCURED
int retryCount = 0;
ccDict[rId].RulePassed = false;
ccDict[rId].RetryAttempts = ccDict[rId].RetryAttempts + 1;
ccDict[rId].ExceptionMessage = "Forced Fail";
ccDict[rId].ReturnCode = -99;
ccDict.TryUpdate(rId, ccDict[rId], ccDict[rId]);
if (ccDict[rId].RetryAttempts >= 5)
{
Console.WriteLine("Failed: " + rId + " Retry Attempts : " + ccDict[rId].RetryAttempts.ToString() + " : " + ccDict[rId].ExceptionMessage.ToString());
cancelToken.Cancel();
}
}
public class myRule
{
public Boolean RulePassed = true;
public string ExceptionMessage = "";
public int RetryAttempts = 0;
public int ReturnCode = 0;
public myRule()
{
RulePassed = false;
ExceptionMessage = "";
RetryAttempts = 0;
ReturnCode = 0;
}
}