I can’t seem to figure out why I am getting an InvalidCastException running the following code:
var item = new KeyValuePair<string, string>('key', 'value'); Action<KeyValuePair<string, string>> kvrAction = kvr =>Console.WriteLine(kvr.Value); var result = kvrAction.BeginInvoke(item, null, null); kvrAction.EndInvoke(result);
Exception Info:
Test method Utilities.Tests.IEnumerableExtensionTests.ProveDelegateAsyncInvokeFailsForKeyValuePair threw exception: System.Runtime.Remoting.RemotingException: The argument type '[key, value]' cannot be converted into parameter type 'System.Collections.Generic.KeyValuePair`2[System.String,System.String]'. ---> System.InvalidCastException: Object must implement IConvertible..
Any assistance would be appreciated =) This code seems to work with anything I throw at it except a KeyValuePair<>.
Update: It appears this condition exists for any struct. I hadn’t noticed KeyValuePair<> was a struct and so was only testing with classes. I still don’t understand why this is the case though.
Update 2: Simon’s answer helped confirm this behavior is unexpected, however implementing a custom type won’t work for what I’m trying to do. I am trying to implement an extension method on IEnumerable<> to execute a delegate Asynchronously for each item. I noticed the error running tests against a generic Dictionary object.
public static IEnumerable<T> ForEachAsync<T>(this IEnumerable<T> input, Action<T> act) { foreach (var item in input) { act.BeginInvoke(item, new AsyncCallback(EndAsyncCall<T>), null); } return input; } private static void EndAsyncCall<T>(IAsyncResult result) { AsyncResult r = (AsyncResult)result; if (!r.EndInvokeCalled) { var d = (Action<T>)((r).AsyncDelegate); d.EndInvoke(result); } }
I would rather not limit the method with a constraint on T to ensure only classes are used so I have refactored the method as follows to get around the problem with BeginInvoke but I have not worked with the TreadPool directly before and would like to make sure I am not missing anything important.
public static IEnumerable<T> ForEachAsync<T>(this IEnumerable<T> input, Action<T> act) { foreach (var item in input) ThreadPool.QueueUserWorkItem(obj => act((T)obj), item); return input; }
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:
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:
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: