I have a json File that I deserialize with Newtonssoft Json.Net like this:
/* Get current config */
dynamic json = JsonConvert.DeserializeObject<Speaker>(
File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "cfg\\speaker.json"));
dynamic jsonDevice = json.DeviceList;
/* Go through the List */
foreach (Tapi tapi in lvTapiSonos.Items)
{
foreach (var line in jsonDevice)
{
foreach (var l in line)
{
/* If not in List already, add it */
if (l.Key != tapi.Name)
{
/* Add to Config */
json.DeviceList.Add(new Dictionary<string, List<Device>>
{
{
tapi.Name,
new List<Device>
{
new Device
{
Volume = "5",
Ip = currentEditIp,
Name = currentEditName
}
}
}
});
}
}
}
}
string output = JsonConvert.SerializeObject(json, Formatting.Indented);
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "cfg\\speaker.json", output);
Unfortunately that only works for the first foreach as I get an exception "The List has changed. Enumeration cannot be continued," (similar as I have it in german) in the line foreach (var line in jsonDevice).
I understand this means, that the jsonDevice has been updated (it shows now one more item in debug), but since I assigned jsonDevice outside of the foreach, how is it updated? Having foreach var line in json.DeviceList produce and error seems logical as I update the json Object inside the foreach, but why does this still happen?
Any hint appreciated...
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a
for loop.
http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx
As explained you can use for loop for your usecase. but if you call ToList() or ToArray you can get copy of items which can be use for iterate
dynamic jsonDevice = json.DeviceList.ToList();
Following is the ilegal code here:
json.DeviceList.Add(new Dictionary<string, List<Device>>
Why ?
In foreach you get an enumerator to the collection, which is read-only forward moving, you cannot change the original source inside the foreach loop. You are accessing the jsonDevice, which is a reference copy of json.DeviceList collection which is modified
What to do:
Convert to for loop and access by index, then you can change any collection as you are accessing by index and not changing the original collection with an attached enumerator
Or
Create a deep copy of the collection, creating a new object and copying all value members and deep copy for all reference type. You may override Memberwiseclone of the base object class
Related
This code:
foreach (var d in data.OfType<FtpData>().Where(fd => !string.IsNullOrEmpty(fd.LocalPath)))
{
d.LocalPath = Url.Content(d.LocalPath);
}
When the loop finishes if I examine 'data' the value of LocalPath is still the original, it has not changed. Why? Does the OfType() create a copy of the object?
The loop is executing correctly - I can breakpoint inside it and see the property value change - but once out of the loop the change is lost.
UPDATE:
If I do this:
var data2 = data.ToList();
and change the foreach to use data2 instead then it works. I don't quite understand why though, but looking in the debugger the actual type of 'data' is System.Linq.Enumerable.WhereSelectEnumerableIterator ...which may have something to do with it?
IEnumerable<T> is NOT for modifying items. It is for iterating items.Use List or Array (.ToList() or .ToArray()).
// Get list from DB
List<Category> dbCategories = DatabaseWrapper.GetCategories();
...
// COPY values to Obs.Col.
var shownCategries = new ObservableCollection<Category>(dbCategories);
// This also changes the value in 'dbCatagories'.
shownCategories[0].Name = "Hej";
I want to be able to change a value in the obs.col. without changing the same value in dbCategories. I can't figure out what I'm doing wrong since the 2nd line of code should be a copy constructor?
Nothing can explain more than source code. You're making invalid assumptions.
Namely:
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = this.Items;
if (collection == null || items == null)
return;
foreach (T obj in collection)
items.Add(obj);
}
and
public ObservableCollection(List<T> list)
: base(list != null ? (IList<T>) new List<T>(list.Count) : (IList<T>) list)
{
this.CopyFrom((IEnumerable<T>) list);
}
as you can see, "copy constructor" indeed does make copy of LIST object itself, but it does not make copy of individual list elements. Note, it's not that simple, your example could actually work if Category would be struct.
This is usual behaviour, and it's called shallow copying. Alternatively, what you are looking is called deep copying
You need to create manually copies, either using serialization, or just create copy-constructor in Category class.
This would be good example:
new ObservableCollection(dbCategories.Select(x => new Category(x)).ToList());
I am trying to remove object while I am iterating through Collection. But I am getting exception. How can I achieve this?
Here is my code :
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(gem.Key); // I can't do this here, then How can I do?
OnGemCollected(gem.Value, Player);
}
}
foreach is designed for iterating over a collection without modifing it.
To remove items from a collection while iterating over it use a for loop from the end to the start of it.
for(int i = gems.Count - 1; i >=0 ; i--)
{
gems[i].Value.Update(gameTime);
if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
Gem gem = gems[i];
gems.RemoveAt(i); // Assuming it's a List<Gem>
OnGemCollected(gem.Value, Player);
}
}
If it's a dictionary<string, Gem> for example, you could iterate like this:
foreach(string s in gems.Keys.ToList())
{
if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(s);
}
}
The easiest way is to do what #IV4 suggested:
foreach (var gem in gems.ToList())
The ToList() will convert the Dictionary to a list of KeyValuePair, so it will work fine.
The only time you wouldn't want to do it that way is if you have a big dictionary from which you are only removing relatively few items and you want to reduce memory use.
Only in that case would you want to use one of the following approaches:
Make a list of the keys as you find them, then have a separate loop to remove the items:
List<KeyType> keysToRemove = new List<KeyType>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
OnGemCollected(gem.Value, Player);
keysToRemove.Add(gem.Key);
}
}
foreach (var key in keysToRemove)
gems.Remove(key);
(Where KeyType is the type of key you're using. Substitute the correct type!)
Alternatively, if it is important that the gem is removed before calling OnGemCollected(), then (with key type TKey and value type TValue) do it like this:
var itemsToRemove = new List<KeyValuePair<TKey, TValue>>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
itemsToRemove.Add(gem);
}
foreach (var item in itemsToRemove)
{
gems.Remove(item.Key);
OnGemCollected(item.Value, Player);
}
As the other answers say, a foreach is designed purely for iterating over a collection without modifying it as per the documenation:
The foreach statement is used to iterate through the collection to get
the desired information, but should not be used to change the contents
of the collection to avoid unpredictable side effects.
in order to do this you would need to use a for loop (storing the items of the collection you need to remove) and remove them from the collection afterwards.
However if you are using a List<T> you could do this:
lines.RemoveAll(line => line.FullfilsCertainConditions());
After going through all the answers, and being equally good. I faced a challenge where I had to modify a List and what I ended up doing worked quite well for me. So just in case anyone finds it useful. Can someone provide me feedback on how efficient it might be.
Action removeFromList;
foreach(var value in listOfValues){
if(whatever condition to remove is){
removeFromList+=()=>listOfValues.remove(value);
}
}
removeFromList?.Invoke();
removeFromList = null;
You should use the for loop instead of the foreach loop. Please refer here
Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.
Use for loop for collection modifying.
I am trying to fill out a pdf form using reflection as below:
// Get the form fields for this PDF and fill them in!
var formFieldMap = GetFormFieldNames(pdfTemplate);
foreach (var fields in formFieldMap)
{
var fieldName = UppercaseFirst(fields.Key.Replace("pdf_", ""));
var proPertyValue = fosques.GetType().GetProperty(fieldName).GetValue(ques, null);
formFieldMap[fields.Key] = proPertyValue.ToString();
}
This works fine for first property, but on second iteration it say Collection was modified; enumeration operation may not execute.
Should I create another collection of fields, or can I avoid this?
You are modifying the formFieldMap inside the foreach loop which is the base for iteration . Thats the reason it stops iterating for second time. So you have to maintain a separate variable to collect those values inside the loop and append it to formFieldMap once you are done with looping.
You are referencing the list you are enumerating through inside the enumeration.
You don't need to get formFieldMapy[fields.Key] because you already have access to it, so you could simply do:
foreach (var fields in formFieldMap)
{
var fieldName = UppercaseFirst(fields.Key.Replace("pdf_", ""));
var proPertyValue = fosques.GetType().GetProperty(fieldName).GetValue(ques, null);
fields.Value = proPertyValue.ToString();
//formFieldMap[fields.Key] = proPertyValue.ToString();
}
The above does the same thing, it just doesn't directly modify what you are enumerating.
In my example code below i would like to replace the item in the dictionary with a new item or assign new values to that item. How can i do this?
This is my code:
dynamic data = jss.Deserialize<dynamic>(jsonText);
foreach (KeyValuePair<string,object> item in data["typeData"])
{
if(item.Key == "somevalue")
{
item = new KeyValuePair<string,object>();
}
}
I'm getting:
Cannot assign to 'item' because it is a 'foreach iteration variable'
There must be a work-around.
foreach are considered to be read only contexts.
Don't use foreach as the message says, convert the loop to a regular for loop.
From MSDN:
This error occurs when an assignment to variable occurs in a read-
only context. Read-only contexts include foreach iteration variables,
using variables, and fixed variables. To resolve this error, avoid
assignments to a statement variable in using blocks, foreach
statements, and fixed statements.
In your case the object item is not a reference its simply a copy hence any change you make to it will not result in a change in the original object.
Depends on what you want. Do you just need to override the value? I assume so because replacing the key and value would be a very different operation (remove one item and insert another)
just iterate over the keys instead of the collection (Assuming it's a dictionary):
dynamic data = jss.Deserialize<dynamic>(jsonText)["typeData"];
foreach (string key in data.Keys)
{
if(key == "somevalue")
{
data[key] = ...;
}
}
if there's no keys property you can substitute that part with (assuming that at least there's an indexer)
foreach (string key in data.Select(pair=>pair.Key)){
...
}
The problem with your code is that you are attempting to change the value of a variable that is used as a placeholder. The variable "item" simply has the same reference that exists in the dictionary; changing the object that "item" references won't actually change anything in the Dictionary itself, and on top of that it can screw up the logic of looping through the Dictionary.
In addition to not being able to reassign the placeholder, you are not allowed to add or remove items from the Dictionary within a foreach loop that uses said Dictionary, because that will also mess up the logic of iterating through the Dictionary's items (the item that is the "current" item of the enumerator behind the scenes now no longer exists, so the enumerator may lose its place in the collection and not be able to continue.
The workaround is to enumerate a different collection when you change the original collection. Basically, a task like this requires two passes; first collect the items you want to change, then enumerate through THAT collection and make the change to the original collection:
...
var itemsToChange = new List<KeyValuePair<string, object>>();
foreach (var item in data["typeData"])
{
if(item.Key == "somevalue")
itemsToChange.Add(item);
}
foreach(var item in itemsToChange)
{
//even here you can't just "swap out" KVPs;
//you must remove the old and add the new
data["typeData"].Remove(item);
data["typeData"].Add(someNewString, someNewObject);
}
You have to either use a for loop or store the variables you want changed and change them outside of the foreach loop.
Perhaps there's something missing from your question, but it seems that the workaround is to avoid looping entirely:
dynamic data = jss.Deserialize<dynamic>(jsonText);
var item = new KeyValuePair<string, object>("somevalue", data["somevalue"]);
or perhaps:
dynamic data = jss.Deserialize<dynamic>(jsonText);
DoSomethingWith(data["somevalue"]);
what's the reason for your loop?