调用 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
包含已编译的 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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看起来框架未能正确锁定 MakeGenericMethod 的内部。
当调用 MakeGenericMethod 时,框架应该使用指定的泛型参数创建该方法的新版本,或者如果之前已使用相同的泛型参数类型来创建该泛型方法,那么它应该返回先前的-生成的方法。看起来您遇到了一个边缘情况,在多个线程上调用
MakeGenericMethod
可能会导致竞争条件,两个线程都认为该方法尚未生成并继续生成它,然后随后发生冲突存储生成的方法以供将来调用。也就是说,在这种情况下,看起来一切都被锁住了,所以我也不完全相信这就是问题所在。
我会将其作为错误向 MSFT 归档,除非其他人可以解释这是如何预期的行为。
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.