2012-02-23 111 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