我们一直使用Simple.Mocking作为单元测试的模拟框架。最近,我对 ICacheService 进行了更改,这在我们的单元测试和模拟中大量使用。
我所做的更改是添加类似于以下的方法:
bool TrySetCache(string key,T data,ref T value);
此实现的语义无关紧要,因此请不要提供有关引用类型参数使用的反馈。 :)
一旦我们的CI构建启动并运行我们的自动化测试,它们都会因模拟类型创建失败而失败。
我深入研究了这个项目,并意识到它在尝试模拟通用引用类型参数时失败了。
以下coee处理为模拟方法参数发出IL:
var baseMethodParameters = baseMethod.GetParameters();
ilGenerator.Emit(OpCodes.Ldc_I4, baseMethodParameters.Length);
ilGenerator.Emit(OpCodes.Newarr, typeof(object));
ilGenerator.Emit(OpCodes.Stloc_2);
for (int i = 0; i < baseMethodParameters.Length; i++)
{
var parameter = baseMethodParameters[i];
var parameterType = parameter.ParameterType;
ilGenerator.Emit(OpCodes.Ldloc_2);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldarg, i + 1);
if (parameterType.IsByRef)
{
parameterType = parameterType.GetRealTypeForByRefType();
ilGenerator.EmitLoadIndirect(parameterType);
}
EmitBoxValue(ilGenerator, parameterType);
ilGenerator.Emit(OpCodes.Stelem_Ref);
}
更具体地说,处理reference-type参数的代码:
if (parameterType.IsByRef)
{
parameterType = parameterType.GetRealTypeForByRefType();
ilGenerator.EmitLoadIndirect(parameterType);
}
当使用通用引用类型参数时,这将调用失败的扩展方法:
const string ByRefSpecifier = "&";
public static Type GetRealTypeForByRefType(this Type type)
{
if (!type.IsByRef)
throw new InvalidOperationException();
return Type.GetType(type.AssemblyQualifiedName.Replace(ByRefSpecifier, string.Empty), true);
}
return语句失败。
我想解决这个问题,但我不确定它是否可以推断使用情况并模拟通用引用类型参数。
如果它实际上是不可能的,那么我最后会添加一个检查以查看它是否是泛型类型参数,如果是,则给出错误消息,以便用户不需要进行太长时间的故障排除找出问题。
重要提示我在调试时注意到的一件事是当它反映在我的ICacheService.TrySetCache(字符串键,T数据,ref T值)时,它没有标记最后一个参数作为通用参数类型。
答案 0 :(得分:1)
由于您的类型是通用参数,AssemblyQualifiedName
将为null。相反,parameterType.GetRealTypeForByRefType()
几乎肯定会被parameterType.GetElementType()
替换。