I have List with objects of strings and doubles, and I try to call different methods based on the itemtype and their value. In the debugger I can see that the first iteration works fine, but an error shows when entering the second time after a method is called.
If i comment out the methods and put in simple methods it works, so I understand that it something with how I call the methods.
What do I do wrong, and what can I do to make it work?
If there is easier ways to do what I'm trying, please let me know.
public double evaluateExpressionUsingVariableValues(List<Object> anExpression, Dictionary<String, double> variables)
{
foreach (object element in anExpression)
{
if(element.GetType()!=typeof(string))
{
setOperand((double)element);
}
else if (element.GetType() == typeof(string))
{
if (!element.ToString().StartsWith("%"))
performOperation((string)element);
else
setOperand(variables[element.ToString()]);
}
}
return this.operand;
}
If your methods (setOperand, performOperation) modify the collection at all, you will get an exception. You can't modify the collection while you are iterating over it. One method is to create a result collection and add items to it as you change them, rather than trying to modify the collection in-place.
private void Foo() {
foreach(var item in items) {
if (item.IsBad) {
DeleteItem(item); // will throw an exception as it tries to modify items
}
}
}
private void DeleteItem(Item item) {
items.Remove(item);
}
Instead, try:
private void Foo() {
List<Item> result = new List<Item>();
foreach(var item in items) {
if (!item.IsBad) {
result.Add(item); // we are adding to a collection other
// than the one we are iterating through
}
}
items = result; // we are no longer iterating, so we can modify
// this collection
}
Are you sure that none of the methods you are calling is modifying the collection (anExpression) ? This kind of problem is often the result of that. Try replacing the foreach by a for loop and see if you still get the same issue.
Related
An error gets thrown that the collection gets modified during the foreach loop.
Considering the only thing that happens in the foreach loop is a method, in which I pass a string. I cannot find a way in which the collection 'cards' could possibly be changed during the loop, regardless of what happens inside the method.
I would like to know if any of you have a definition for the ExcludeCard method that could change the cards collection.
After some great comments by others, I would like to add that this method is called by another class. The Lack method has no way of referring back to the class that called it.
public void Lack(List<string> cards, int playerIndex)
{
foreach (string card in cards)
{
ExcludeCard(card, playerIndex);
}
}
You can cause this error to occur trivially if your implementation of ExcludeCard modifies the collection that is passed to your Lack method. This is because a List<T> is passed by reference, so you you would be acting on a collection that's currently being enumerated.
The following will cause the issue:
void Main()
{
Lack(Cards, 0);
}
public List<string> Cards = Enumerable.Range(0,10).Select(x => x.ToString()).ToList();
public void Lack(List<string> cards, int playerIndex)
{
foreach (string card in cards)
{
ExcludeCard(card, playerIndex);
}
}
public void ExcludeCard(string card, int playerIndex)
{
Cards.Remove(card);
}
Say I have the following methods in my code:
public bool RemoveItem(Item item)
{
// Logic to remove a single item
}
public bool RemoveItems(List<Item> items)
{
// Logic for removing multiple items. Running over all items and calling RemoveItem will be inefficient in my case
}
public bool AddItem(Item item)
{
// Logic for adding single item
}
public bool AddItems(List<Item> items)
{
// Logic for adding multiple items
}
Is there a way to prevent having multiple methods for each operation? I have alot of such methods. I wish to somehow combine each couple of methods into a single one..is there a nice way for doing so?
I can create a list with single item and support only methods that take list as parameter but is seems ugly to me.
How do other people do it?
You can create your methods with params keyword:
public bool AddItems(params Item[] items)
{
...
}
public bool RemoveItems(params Item[] items)
{
...
}
This allows you call these methods like this:
AddItems(item);
AddItems(item1, item2, ...);
or
AddItems(new Item[] { ... });
Your approach is right. This is the way it's implemented in the list class, where you have Add() and AddRange()
Better to get a list every time no matter if you have one or many items and handle in a single funstion.
Is there a smarter way of protecting foreach loops against NullReference exceptions than this:
if (G_Locatie.OverdrachtFormulierList != null)
{
foreach (OverdrachtFormulier otherform in G_Locatie.OverdrachtFormulierList)
{
...
}
}
I use a lot of foreach loops, often nested, and a lot of variables where e.g. G_Location certainly exists, but datamember .OverdrachtFormulierList may not have been assigned a list use new yet.
Dear friends, thanks for all your comments. After getting the idea of your suggestions, while having a lot of trouble understanding exactly, after digging through the Lasagna code I got to work on, and after some experimentation, I found that the easiest and cleanest way is to simply avoid having the NULL, by proper initialization. While I kind of resist having to initialize the OverdrachtFormulierList in my code, with the risk of forgetting one instance, I found the proper place for initialization, namely in the original class definition.
For simplicity, look at this code:
class MyClass
{
public List<string> items = new List<string>();
public IEnumerator<string> GetEnumerator()
{
return items.GetEnumerator();
}
}
class MyComplexClass
{
private MyClass _itemlist /*= new MyClass()*/;
public MyClass itemlist
{
get { return _itemlist; }
set { _itemlist = value; }
}
}
void Sandbox()
{
MyClass mc /*= new MyClass()*/;
foreach (string Sb in mc.items)
{
string x = Sb;
}
MyComplexClass mcc = new MyComplexClass();
foreach (string Mb in mcc.itemlist) // <--- NullReferenceException
{
string x = Mb;
}
return;
}
The fun thing is that C# seems to protect you from a lot of buggy mistakes. This code will not build if you do not uncomment the initialization in Sandbox(), so the first foreach will not get a NullReferenceException.
However, you'd better uncomment the init in MyComplexClass to avoid the exception in the second foreach. C# will build with and without this initialization.
So it turns out that in my real code I just have to add a simple initialization in the Class definition of G_Locatie.
The only issue now is that I always wanted to simplify the above code with {get; set;} but that would not be possible with the initialization as described. I will have to live with that minor issue.
In fact, on object-type properties, you don't really need the setter.
Finally, I realized that I could not find a proper title for my problem. So far, every problem I had was already answered in this forum, and I feel that I had to post today only because I could not find posts similar to this one. Perhaps someone can come up with title and tags that make this solution better findable.
Yes, your collection properties should return empty collections rather than null. One way you can ensure this is by using a backing field and assigning a new list in the getter:
private List<string> overdrachtFormulierList;
public List<string> OverdrachtFormulierList
{
get
{
return this.overdrachtFormulierList ??
(this.overdrachtFormulierList = new List<string>());
}
set
{
this.overdrachtFormulierList = value;
}
}
You can also use Enumerable.Empty<T> if your types are IEnumerable<T>
One option would be to create an extension method:
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable source)
{
return source ?? Enumerable.Empty<T>();
}
Then:
foreach (var otherform in G_Locatie.OverdrachtFormulierList.EmptyIfNull())
{
...
}
It would still be preferable to always use an empty collection instead of a null reference, mind you.
Is there a one line LINQ statement that would replace the foreach loop in the code below?
public static ReplaceItemsOnOrder(Order order, List<OrderItem> replacements)
{
order.Items.Clear();
foreach (var item in replacements)
{
order.Items.Add(item);
}
}
EDIT:
Here is a simplified definition for the Order class:
public class Order
{
private Collection<OrderItem> _items = new Collection<OrderItem>();
public Collection<OrderItem> Items
{
get { return _items; }
}
}
You could write the following:
order.Items.Clear();
replacements.ForEach(order.Items.Add);
Alternatively if there's an addRange method (available on List<T>):
order.Items.Clear();
order.Items.AddRange(replacements);
It's not LINQ but there's AddRange
order.Items.AddRange(replacements);
But you haven't said what Items is, so unless it's a List that method won't be available.
No, Linq is about selecting, not modifying.
You could write your own extension method to add this feature though.
Is order.Items a List? You could have:
public static ReplaceItemsOnOrder(Order order, List<OrderItem> replacements)
{
order.Items = new List<OrderItem>(replacements);
}
This makes sense because, in your example code, it seems you're replacing the items in order.Items. List<T> has a constructor which accepts an IEnumerable<T> argument, whose contents will be copied to the list being constructed.
It's also safer in the sense that if an error occurrs at construction time (including the copy operation), it won't result in a half-full new list.
I don't know why I have an IndexOutOfRangeException when I am clearing a System.Collections.Generic.List<T>. Does this make sense?
List<MyObject> listOfMyObject = new List<MyObject>();
listOfMyObject.Clear();
This typically happens if multiple threads are accessing the list simultaneously. If one thread deletes an element while another calls Clear(), this exception can occur.
The "answer" in this case is to synchronize this appropriately, locking around all of your List access.
Edit:
In order to handle this, the simplest method is to encapsulate your list within a custom class, and expose the methods you need, but lock as needed. You'll need to add locking to anything that alters the collection.
This would be a simple option:
public class MyClassCollection
{
// Private object for locking
private readonly object syncObject = new object();
private readonly List<MyObject> list = new List<MyObject>();
public this[int index]
{
get { return list[index]; }
set
{
lock(syncObject) {
list[index] = value;
}
}
}
public void Add(MyObject value)
{
lock(syncObject) {
list.Add(value);
}
}
public void Clear()
{
lock(syncObject) {
list.Clear();
}
}
// Do any other methods you need, such as remove, etc.
// Also, you can make this class implement IList<MyObject>
// or IEnumerable<MyObject>, but make sure to lock each
// of the methods appropriately, in particular, any method
// that can change the collection needs locking
}
Are you sure that that code throws an exception? I have
using System.Collections.Generic;
class MyObject { }
class Program {
static void Main(string[] args) {
List<MyObject> listOfMyObject = new List<MyObject>();
listOfMyObject.Clear();
}
}
and I do not get an exception.
Is your real-life example more complex? Perhaps you have multiple threads simultaneously accessing the list? Can we see a stack trace?
List<T>.Clear is really quite simple. Using Reflector:
public void Clear() {
if (this._size > 0) {
Array.Clear(this._items, 0, this._size);
this._size = 0;
}
this._version++;
}
In the case when the list already empty, that is not going to ever throw an exception. However, if you are modifying the list on another thread, Array.Clear could throw an IndexOutOfRangeException exception. So if another thread removes an item from the list then this._size (the number of items to clear) will be too big.
The documentation doesn't mention any Exception this method throws, your problem is probably elsewhere.
List<T>.Clear