为何这段Reflection.Emit代码会引发AccessViolationException?
Reflection.Emit代码触发AccessViolationException的原因及解决方法
通常来说,托管代码不应引发访问冲突,这类问题一般出现在原生代码或unsafe代码中。但一段看似简单的Reflection.Emit代码却触发了AccessViolationException,报错位置在
((IEnumerable)sorted).Cast<object>().ToArray();这一行。
问题代码
using System.Collections; using System.Reflection; using System.Reflection.Emit; namespace Repro { internal class Program { static void Main() { TypeBuilder typeBuilder = BuildType(); var realType = typeBuilder.CreateType(); var arr = Array.CreateInstance(realType, 10); var caster = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(realType); var typedArr = caster.Invoke(null, [arr]); var sorter = realType.GetMethod("Sort")!; var sorted = sorter.Invoke(null, [typedArr])!; var bomb = ((IEnumerable)sorted).Cast<object>().ToArray(); // 此处触发异常 } private static TypeBuilder BuildType() { AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new("MyAssembly"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyAssembly"); var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public, typeof(ValueType)); var field = typeBuilder.DefineField("field", typeof(int), FieldAttributes.Public); var extractor = typeBuilder.DefineMethod("GetField", MethodAttributes.Private | MethodAttributes.Static, typeof(int), [typeof(int)]); var il = extractor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ret); var enumType = typeof(IEnumerable<>).MakeGenericType(typeBuilder); var impl = typeBuilder.DefineMethod("Sort", MethodAttributes.Public | MethodAttributes.Static, enumType, [enumType]); il = impl.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldtoken, extractor); var getMethod = typeof(MethodBase).GetMethod("GetMethodFromHandle", [typeof(RuntimeMethodHandle)])!; il.Emit(OpCodes.Call, getMethod); var orderFunc = typeof(Enumerable).GetMethods().Single(m => m.Name == "OrderBy" && m.GetParameters().Length == 2); il.Emit(OpCodes.Call, orderFunc.MakeGenericMethod(typeBuilder, typeof(int))); // 调用Enumerable.OrderBy(enumerable, extractor) il.Emit(OpCodes.Ret); return typeBuilder; } } }
预期等效的原生代码
using System.Linq; struct MyType { public int field; private static int GetField(MyType value) => value.field; public static IEnumerable<MyType> Sort(IEnumerable<MyType> values) { return values.OrderBy(MyType.GetField); } } // 在Main中 var arr = new MyType[10]; var sorted = MyType.Sort(arr); var bomb = sorted.ToArray();
原因分析
出现AccessViolationException的核心原因有两个:
GetField方法参数类型错误
你定义的GetField方法参数类型是int,但实际需要的是动态生成的MyType结构体类型。这导致IL代码中尝试对int类型执行Ldfld操作(访问MyType的field字段),相当于把int的内存当作结构体来解析,直接访问了非法的内存地址,这是触发访问冲突的直接原因。传递给OrderBy的参数类型不匹配
在Sort方法的IL中,你通过MethodBase.GetMethodFromHandle获取了方法的MethodBase实例,但Enumerable.OrderBy需要的是Func<MyType, int>委托类型。直接传递MethodBase对象会导致类型不匹配,运行时调用委托时会出现内存访问错误。
解决方法
修正上述两个错误即可解决问题:
1. 修正GetField方法的参数类型
将GetField方法的参数类型从typeof(int)改为动态生成的typeBuilder(即MyType):
var extractor = typeBuilder.DefineMethod("GetField", MethodAttributes.Private | MethodAttributes.Static, typeof(int), new[] { typeBuilder });
2. 正确创建委托并传递给OrderBy
替换Sort方法中获取MethodBase的代码,改为创建正确的Func<MyType, int>委托:
// 替换原来的Ldtoken和GetMethodFromHandle代码块 il.Emit(OpCodes.Ldnull); // 静态方法,目标对象为null il.Emit(OpCodes.Ldtoken, extractor); var methodHandleValueProp = typeof(RuntimeMethodHandle).GetProperty(nameof(RuntimeMethodHandle.Value))!.GetGetMethod(); il.Emit(OpCodes.Call, methodHandleValueProp); var funcType = typeof(Func<,>).MakeGenericType(typeBuilder, typeof(int)); var createDelegateMethod = typeof(Delegate).GetMethod(nameof(Delegate.CreateDelegate), new[] { typeof(Type), typeof(object), typeof(IntPtr) })!; il.Emit(OpCodes.Call, createDelegateMethod);
修正后的完整BuildType方法
private static TypeBuilder BuildType() { AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new("MyAssembly"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyAssembly"); var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public, typeof(ValueType)); var field = typeBuilder.DefineField("field", typeof(int), FieldAttributes.Public); // 修正参数类型为typeBuilder var extractor = typeBuilder.DefineMethod("GetField", MethodAttributes.Private | MethodAttributes.Static, typeof(int), new[] { typeBuilder }); var il = extractor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ret); var enumType = typeof(IEnumerable<>).MakeGenericType(typeBuilder); var impl = typeBuilder.DefineMethod("Sort", MethodAttributes.Public | MethodAttributes.Static, enumType, [enumType]); il = impl.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // 正确创建Func委托 il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldtoken, extractor); var methodHandleValueProp = typeof(RuntimeMethodHandle).GetProperty(nameof(RuntimeMethodHandle.Value))!.GetGetMethod(); il.Emit(OpCodes.Call, methodHandleValueProp); var funcType = typeof(Func<,>).MakeGenericType(typeBuilder, typeof(int)); var createDelegateMethod = typeof(Delegate).GetMethod(nameof(Delegate.CreateDelegate), new[] { typeof(Type), typeof(object), typeof(IntPtr) })!; il.Emit(OpCodes.Call, createDelegateMethod); var orderFunc = typeof(Enumerable).GetMethods().Single(m => m.Name == "OrderBy" && m.GetParameters().Length == 2); il.Emit(OpCodes.Call, orderFunc.MakeGenericMethod(typeBuilder, typeof(int))); il.Emit(OpCodes.Ret); return typeBuilder; }
内容的提问来源于stack exchange,提问作者Mason Wheeler
相关产品推荐
相关产品推荐

