Odd, seems to be some sort of bug in .NET (C#?) with marshalling the argument to the worker thread.
If you implement IConvertable on the passed struct:
struct MyPair<TKey, TValue> : IConvertable
{
public readonly TKey Key;
public readonly TValue Value;
public MyPair(TKey key, TValue value)
{
Key = key;
Value = value;
}
// I just used the smart-tag on IConvertable to get all these...
// public X ToX(IFormatProvider provider) { throw new InvalidCastException(); }
...
public object ToType(Type conversionType, IFormatProvider provider)
{
if (typeof(MyPair<TKey, TValue>).GUID == conversionType.GUID)
return this;
throw new InvalidCastException();
}
}
It runs fine. The passed conversionType doesnt pass .Equal(), IsAssignableFrom(), or anything else I tried except GUID comparison, which is probably related to why it asks for an IConvertable in the first place.
EDIT: A simple workaround is to use closures to pass the parameter:
var data = new Dictionary<string, string> {
{ "Hello", "World" },
{ "How are", "You?" },
{ "Goodbye", "World!" }
};
foreach (var pair in data)
{
var copy = pair; // define a different variable for each worker
Action worker = () => Console.WriteLine("Item {0}, {1}", copy.Key, copy.Value);
worker.BeginInvoke(null, null);
}
Of course, if you need the results, you will need to store the IAsyncResults, which will probably have the same issue as parameters, in the other direction. As an alternative, you could add them to a collection when they are complete, but the locking gets a bit weird:
var data = new Dictionary<string, string> {
{ "Hello", "World" },
{ "How are", "You?" },
{ "Goodbye", "World!" }
};
var results = new List<KeyValuePair<string, string>>();
var pending = 0;
var done = new ManualResetEvent(false);
var workers = new List<Action>();
foreach (var pair in data)
{
++pending;
var copy = pair; // define a different variable for each worker
workers.Add(delegate()
{
Console.WriteLine("Item {0}, {1}", copy.Key, copy.Value);
lock (results)
results.Add(new KeyValuePair<string, string>("New " + copy.Key, "New " + copy.Value));
if (0 == Interlocked.Decrement(ref pending))
done.Set();
});
}
foreach (var worker in workers)
worker.BeginInvoke(null, null);
done.WaitOne();
foreach (var pair in results)
Console.WriteLine("Result {0}, {1}", pair.Key, pair.Value);