在 运行 时间内创建的方法会根据它的状态更改另一个方法调用的参数顺序 运行
Method created during runtime changes the parameter order of another method call depending on how it is run
我在 运行 期间使用 Reflection.Emit
实现接口并创建它们定义的方法。
接口中方法的示例定义:
IFoo DoSomething(IBar bar, string name);
要创建方法,我执行以下操作:
var args = methodInfo.GetParameters();
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType, (from arg in args select arg.ParameterType).ToArray());
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
var generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldBuilder);
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
if (args.Any())
{
generator.Emit(OpCodes.Ldc_I4_S, args.Length);
generator.Emit(OpCodes.Newarr, typeof(object));
for (int i = 0; i < args.Length; i++)
{
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Ldc_I4_S, i);
generator.Emit(OpCodes.Ldarg_S, i + 1);
generator.Emit(OpCodes.Stelem_Ref);
}
}
else
{
generator.Emit(OpCodes.Ldc_I4_0);
}
generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Ret);
例如,这会生成我的 DoSomething(IBar bar, string name)
接口方法。
所有生成的方法都调用ISomeType
.
的方法Test()
这是方法Test()
:
public object Test(Type type, object[] arguments)
{
//do something
}
当我 运行 我的应用程序并调用 DoSomething()
方法时,参数以错误的方式(第一个 object[]
和 Type
第二个)传递给 Test()
,这显然会导致异常。
但是当我 运行 进行单元测试并调用 DoSomething()
方法时,参数被正确传递(首先是 Type
,然后是 object[]
)到 Test()
.
为什么参数传递给 Test()
方法的顺序会有所不同,具体取决于 运行?
因此,在评论中 Marc Gravell 的大量帮助下,我找到了解决此问题的方法。
首先,我在创建方法时遇到了错误。
方法没有参数的情况必须从
改变
generator.Emit(OpCodes.Ldc_I4_0);
将 0
作为 Int32
压入堆栈,
MethodInfo emptyArray = typeof(Array).GetMethod(nameof(Array.Empty))?.MakeGenericMethod(typeof(object));
generator.EmitCall(OpCodes.Call, emptyArray, null);
这实际上是将一个空数组压入堆栈,并将其作为参数传递给 Test()
函数。
要解决参数顺序错误的问题,在我的具体情况下有两种可能的解决方案:
(1)对生成的IL代码的更多改动:
真正将方法的 return 类型推送为 Type
而不是 RuntimeTypeHandle
:
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
MethodInfo getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
generator.EmitCall(OpCodes.Call, getTypeFromHandle, null);
在 returning:
之前将 Test()
的 return 类型转换为所需的 return 类型的方法
generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Castclass, methodInfo.ReturnType);
generator.Emit(OpCodes.Ret);
(2)将Test()
方法转换为泛型方法:
如果您有参数作为 Type
,您可以将您的方法转换为通用方法:
public T Test<T>(object[] arguments)
{
//do something
}
如果您决定这样做,您还必须更改一些 IL 代码生成:
不要将方法的 return 类型压入堆栈:
删除这一行:
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
更改Test()
的调用:
MethodInfo test = typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] { typeof(object[]) }).MakeGenericMethod(methodInfo.ReturnType);
generator.EmitCall(OpCodes.Callvirt, test, null);
generator.Emit(OpCodes.Ret);
在这种情况下,您也不必强制转换 return 值,因为您的方法 Test()
已经 return 具有 T
的正确类型。
对于这两种解决方案中的任何一种,Test()
方法都会以正确的参数顺序进行调用,而不管它是如何调用的(应用程序或单元测试)。
另一个 of Marc Gravell was to use Sigil 在您生成的 IL 代码无效时获得清晰的错误消息。而且无论你选择上面两种解决方案中的哪一种,我都只能推荐这样做。
我在 运行 期间使用 Reflection.Emit
实现接口并创建它们定义的方法。
接口中方法的示例定义:
IFoo DoSomething(IBar bar, string name);
要创建方法,我执行以下操作:
var args = methodInfo.GetParameters();
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType, (from arg in args select arg.ParameterType).ToArray());
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
var generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldBuilder);
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
if (args.Any())
{
generator.Emit(OpCodes.Ldc_I4_S, args.Length);
generator.Emit(OpCodes.Newarr, typeof(object));
for (int i = 0; i < args.Length; i++)
{
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Ldc_I4_S, i);
generator.Emit(OpCodes.Ldarg_S, i + 1);
generator.Emit(OpCodes.Stelem_Ref);
}
}
else
{
generator.Emit(OpCodes.Ldc_I4_0);
}
generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Ret);
例如,这会生成我的 DoSomething(IBar bar, string name)
接口方法。
所有生成的方法都调用ISomeType
.
Test()
这是方法Test()
:
public object Test(Type type, object[] arguments)
{
//do something
}
当我 运行 我的应用程序并调用 DoSomething()
方法时,参数以错误的方式(第一个 object[]
和 Type
第二个)传递给 Test()
,这显然会导致异常。
但是当我 运行 进行单元测试并调用 DoSomething()
方法时,参数被正确传递(首先是 Type
,然后是 object[]
)到 Test()
.
为什么参数传递给 Test()
方法的顺序会有所不同,具体取决于 运行?
因此,在评论中 Marc Gravell 的大量帮助下,我找到了解决此问题的方法。
首先,我在创建方法时遇到了错误。
方法没有参数的情况必须从
改变generator.Emit(OpCodes.Ldc_I4_0);
将 0
作为 Int32
压入堆栈,
MethodInfo emptyArray = typeof(Array).GetMethod(nameof(Array.Empty))?.MakeGenericMethod(typeof(object));
generator.EmitCall(OpCodes.Call, emptyArray, null);
这实际上是将一个空数组压入堆栈,并将其作为参数传递给 Test()
函数。
要解决参数顺序错误的问题,在我的具体情况下有两种可能的解决方案:
(1)对生成的IL代码的更多改动:
真正将方法的 return 类型推送为
Type
而不是RuntimeTypeHandle
:generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType); MethodInfo getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle)); generator.EmitCall(OpCodes.Call, getTypeFromHandle, null);
在 returning:
之前将Test()
的 return 类型转换为所需的 return 类型的方法generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null); generator.Emit(OpCodes.Castclass, methodInfo.ReturnType); generator.Emit(OpCodes.Ret);
(2)将Test()
方法转换为泛型方法:
如果您有参数作为 Type
,您可以将您的方法转换为通用方法:
public T Test<T>(object[] arguments)
{
//do something
}
如果您决定这样做,您还必须更改一些 IL 代码生成:
不要将方法的 return 类型压入堆栈:
删除这一行:
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
更改
Test()
的调用:MethodInfo test = typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] { typeof(object[]) }).MakeGenericMethod(methodInfo.ReturnType); generator.EmitCall(OpCodes.Callvirt, test, null); generator.Emit(OpCodes.Ret);
在这种情况下,您也不必强制转换 return 值,因为您的方法
Test()
已经 return 具有T
的正确类型。
对于这两种解决方案中的任何一种,Test()
方法都会以正确的参数顺序进行调用,而不管它是如何调用的(应用程序或单元测试)。
另一个