Что означает эта ключевая ошибка при вызове MethodInfo.MakeGenericMethod? - PullRequest
2 голосов
/ 23 февраля 2012

Сегодня я получил эту ошибку из некоторого старого кода динамического приведения (я изменил последнюю строку и пропустил остальную часть трассировки стека):

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)

Полный класс

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 - это Dictionary<Type,Func<Type,string>>, содержащий скомпилированные лямбда-выражения, которые лениво создаются по мере необходимости для преобразования событий Message (простых классов авто-свойств на основе EventArgs) в строковый формат, используемый в других системах. Я не думаю, что все это имеет значение, хотя. Я думаю, что единственный код, необходимый для возникновения этой проблемы:

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 Ответ

2 голосов
/ 23 февраля 2012

На что это похоже, это сбой в платформе для правильной блокировки внутренних компонентов MakeGenericMethod.

Когда вызывается MakeGenericMethod, предполагается, что фреймворк либо создаст новую версию метода с указанными универсальными параметрами, либо, если для создания этого универсального метода использовались те же самые универсальные типы параметров, то он должен вернуть ранее сгенерированный метод. Похоже, вы попали в крайний случай, когда вызов MakeGenericMethod в нескольких потоках может привести к состоянию гонки, когда оба потока думают, что метод еще не сгенерирован, и продолжают его генерировать, а затем конфликтуют в сохранении сгенерированных методов для будущие звонки.

Тем не менее, в данном случае похоже, что все в замке, поэтому я не до конца убежден, что это тоже проблема.

Я бы подал его в MSFT как ошибку, если кто-то еще не может объяснить, как это ожидается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...