I often find it’s not really specified what exact collection is causing this type of exception. Is that true or should it be obvious? Perhaps I just don’t understand how to interpret the exception message properly..
Specifically I wonder about this one. What is the collection it refers to?
The parameter of the event delegate is simply (object sender), and the events raised passes null parameter. Though the class itself that raises the event inherits a list:
public class TimeSerie : List<BarData>
Is it clear here if the “collection” refers to the object that raises the event, or can it be another object? Can it be, say a collection of event handlers to a method that is being dynamically changed? Or would that create a different exception?
************** Exception Text **************
System.InvalidOperationException:
Collection was modified; enumeration operation may not execute.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at SomeNameSpace.SomeUserControl.InvokeOnUpdateHistory(Object sender) in D:\SomePath\SomeUserControl.cs:line 5179
at OtherNameSpace.OtherClass.TimeSerie.HistoryUpdateEventHandler.Invoke(Object sender)
Exception occurs in UserControl:
public class SomeUserControl
private void InvokeOnUpdate(object sender)
{
this.Invoke(new GenericInvoker(Method)); // << Exception here!
}
private void Method() {...}
EDIT:
Added some code. A bit simplified, but think it includes relevant bits.
private void Method()
{
if (this.instrument == null) return;
UnRegisterTimeSerieHandlers(this.ts);
this.ts = instrument.DataSeries.GetTimeSerieByInterval(interval);
if (ts != null)
{
RegisterTimeseriesHandlers(ts);
ClearAndLoadAllHistory();
}
}
private void UnRegisterTimeSerieHandlers(TimeSerie ts)
{
if (ts != null)
{
ts.TickUpdate -= InvokeUpdateCurrentBar;
ts.NewBarUpdate -= InvokeUpdateNewBar;
ts.HistoryUpdate -= InvokeOnUpdateHistory;
this.ts = null;
}
}
private void RegisterTimeseriesHandlers(TimeSerie ts)
{
ts.TickUpdate += InvokeUpdateCurrentBar;
ts.NewBarUpdate += InvokeUpdateNewBar;
ts.HistoryUpdate += InvokeOnUpdateHistory;
}
Yes, the cause of an exception can be quite hard to diagnose when you use Control.Invoke(). The problem is that it catches the exception when it occurs on the UI thread and re-throws it in your worker thread. Necessarily so, your worker thread needs to know that the return value of Invoke() is not usable. The inevitable side-effect is that you lose the Holy Stack Trace that tells you where it blew up and how it got there.
If you can repro the problem when you have a debugger attached then use Debug + Exceptions, tick the Thrown checkbox for CLR Exceptions. The debugger stops when the exception is thrown, giving you a good statement location and a call stack to look at.
If not then do consider using Control.BeginInvoke() instead. This is a fire-and-forget version of Invoke() so if the invoked method throws then that exception will be raised on the UI thread and you’ll get an accurate stack trace with it.
In general you always want to favor BeginInvoke(). It doesn’t cause the worker thread to stall, it avoids many deadlock scenarios and give good exception feedback. Using Invoke() is generally a mistake.