What does this key error mean when calling MethodInfo.MakeGenericMethod?

人走茶凉 提交于 2019-12-21 21:17:46

问题


Today I got this error from some old dynamic casting code (I have changed the last line and left out the rest of the stack trace):

Item has already been added. 
Key in dictionary: 
   'Int32 Count[Object](System.Collections.Generic.IEnumerable`1[System.Object])'  
Key being added: 
   'Int32 Count[Object](System.Collections.Generic.IEnumerable`1[System.Object])' 
---> System.ArgumentException: Item has already been added. 
     Key in dictionary: 
         'Int32 Count[Object](System.Collections.Generic.IEnumerable`1[System.Object])'  
     Key being added: 
         'Int32 Count[Object](System.Collections.Generic.IEnumerable`1[System.Object])'
  at System.Reflection.CerHashtable`2.Insert(K[] keys, V[] values, Int32& count, K key, V value)
  at System.Reflection.CerHashtable`2.Preallocate(Int32 count)
  at System.RuntimeType.RuntimeTypeCache.GetGenericMethodInfo(RuntimeMethodHandle genericMethod)
  at System.RuntimeType.GetMethodBase(RuntimeTypeHandle reflectedTypeHandle, RuntimeMethodHandle methodHandle)
  at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
  at MyNamespace.CommunicationExtensions.BuildMessage[T](T obj)

The full class

public static class CommunicationExtensions {
    static readonly object lockobj = new object();
    public static bool CanBuildMessage<T>(this T obj) where T: class {
        return obj != null && (MessageFactory.MessageBuilders.ContainsKey(obj.GetType()));
    }
    public static string BuildMessage<T>(this T obj) {
        lock (lockobj) {
            Delegate d;
            var type = obj.GetType();

            if (MessageFactory.MessageBuilders.TryGetValue(type, out d)) {
                var castMethod = typeof(CommunicationExtensions).GetMethod("Cast").MakeGenericMethod(type);
                var castedObject = castMethod.Invoke(null, new object[] { obj });

                return d.DynamicInvoke(castedObject) as string;
            }
        }
        return null;
    }
    public static T Cast<T>(object o) {
        return (T)o;
    }
}

MessageFactory.MessageBuilders is a Dictionary<Type,Func<Type,string>> containing compiled lambda expressions that are lazily built as needed to convert the Message events (simple auto-property classes based on EventArgs) into a string format used in other systems. I don't think any of that matters though. I think the only code necessary to cause this issue is:

public static class CastError{
    public static void GetCast<T>(this T obj) {
        var type = obj.GetType();
        var castMethod = typeof(CastError).GetMethod("Cast").MakeGenericMethod(type);
        //...
    }
    public static T Cast<T>(object o) {
        return (T)o;
    }
}

回答1:


What it looks like is a failure in the framework to lock properly in the internals of MakeGenericMethod.

When MakeGenericMethod is called, the framework is supposed to either create a new version of the method with the generic parameters specified, or if the same generic parameter type(s) have been used before to create that generic method then it should return the previously-generated method. It looks like you hit an edge case where calling MakeGenericMethod on multiple threads can result in a race condition where both threads think that the method has not yet been generated and go ahead and generate it, then subsequently conflict in storing the generated methods for future calls.

That said, in this case it looks like it's all in a lock, so I am not fully convinced that this is the problem either.

I'd file it with MSFT as a bug, unless someone else can explain how this is expected behavior.



来源:https://stackoverflow.com/questions/9419464/what-does-this-key-error-mean-when-calling-methodinfo-makegenericmethod

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!