2012-02-23 42 views
2

今天我接到了一些旧的动态铸造代码此错误(我已经改变了最后一行,并留出了堆栈跟踪的其余部分):调用MethodInfo.MakeGenericMethod时,这个关键错误意味着什么?

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>>包含编译的lambda表达式,它们是根据需要构建的,以便将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; 
    } 
} 
+0

哇,这是丑陋的。看起来像一个框架错误给我。 MethodInfo被记录为线程安全的。这是多线程代码? – 2012-02-23 19:09:00

+0

是的,DynamicInvoke在那里调用一些不一定线程安全的东西,虽然(被转换的类的get方法可以做任何事情)。 – 2012-02-23 19:13:09

+1

@BillBarry那是真的,但堆栈跟踪显式地显示来自MakeGenericMethod调用的这个错误... – 2012-02-23 19:14:59

回答

2

它看起来像什么是框架未能在MakeGenericMethod的内部正确地锁定。

当调用MakeGenericMethod时,框架应该创建一个指定了泛型参数的方法的新版本,或者如果在创建泛型方法之前使用了相同的泛型参数类型,那么它应该返回以前生成的方法。它看起来像是碰到了一个边缘情况,在多线程上调用MakeGenericMethod可能会导致竞态条件,两个线程都认为该方法尚未生成并继续生成,然后在为将来的调用存储生成的方法时发生冲突。

这就是说,在这种情况下,它看起来像是全部锁在一起,所以我不完全相信这是问题。

我会把它作为一个错误的MSFT文件,除非别人可以解释如何这是预期的行为。

+0

我在.NET 4.0中也遇到了这个bug,并将其作为https://connect.microsoft.com/VisualStudio/feedback/details/738253归档 – 2012-04-23 11:46:07