我们一直在使用Simple.Moking作为单元测试的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);
}
更具体地说,处理引用类型参数的代码:
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值)上时,它不会将最后一个参数标记为通用参数类型。
由于类型是泛型参数,AssemblyQualifiedName
将为null。相反,几乎可以肯定的是,parameterType.GetRealTypeForByRefType()
应该被parameterType.GetElementType()
所取代。