代码之家  ›  专栏  ›  技术社区  ›  Venr

CastException尝试异步调用Action<KeyValuePair<>>委托

  •  1
  • Venr  · 技术社区  · 17 年前

    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);
    

    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..
    

    任何帮助都将不胜感激=)除了KeyValuePair之外,这段代码似乎适用于我扔给它的任何东西<>。

    更新:似乎任何结构都存在这种情况。我没有注意到KeyValuePair<>是一个结构体,因此只对类进行测试。但我仍然不明白为什么会这样。

        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);
            }
        }
    

    我宁愿不使用T约束来限制该方法,以确保只使用类,所以我按照如下方式重构了该方法以解决BeginInvoke的问题,但我以前没有直接使用过TreadPool,我想确保我没有遗漏任何重要的东西。

        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;
        }
    
    1 回复  |  直到 17 年前
        1
  •  1
  •   Simon Buchan    17 年前

    奇怪,好像有什么毛病。NET(C#?),将参数编组到工作线程。

    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();
        }
    }
    

    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);
    }
    

    当然,如果你需要结果,你需要在另一个方向上存储IAsyncResults,它可能与参数有同样的问题。作为替代方案,您可以在它们完成时将它们添加到集合中,但锁定有点奇怪:

    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);