为什么此 Reflection.Emit 代码会导致 AccessViolationException?

问题描述 投票:0回答:1

作为一般规则,托管代码不应导致访问冲突。这发生在本机代码或

unsafe
代码中。但不知何故,通过一些看起来应该太简单而不会出错的 Reflection.Emit 代码,我遇到了访问冲突。

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(); //blows up here
        }

        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))); // call 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);
   }
}

//in Main

    var arr = new MyType[10];
    var sorted = MyType.Sort(arr);
    var bomb = sorted.ToArray();

但不知怎的,在

bomb
线上,它抛出了一个
AccessViolationException
。我很想了解原因以及如何解决它。

c# .net-core reflection.emit
1个回答
0
投票

Enumerable.OrderBy()
不接受方法句柄作为参数,我认为您错过了 C# 如何隐式为您创建
Func<T,int>

也许你应该尝试生成类似的代码;

struct MyType
{
   public int field;
   private static int GetField(MyType value) => value.field;
   private static Func<MyType, int> func;
   public static IEnumerable<MyType> Sort(IEnumerable<MyType> values)
   {
      return values.OrderBy(func ?? (func = new Func<MyType,int>(GetField)));
   }
}

根据 sharplab.io,它应该类似于以下 IL;

ldarg.0
ldsfld class [System.Runtime]System.Func`2<valuetype MyType, int32> MyType::func
dup
brtrue.s IL_001c

pop
ldnull
ldftn int32 MyType::GetField(valuetype MyType)
newobj instance void class [System.Runtime]System.Func`2<valuetype MyType, int32>::.ctor(object, native int)
dup
stsfld class [System.Runtime]System.Func`2<valuetype MyType, int32> MyType::func

call class [System.Linq]System.Linq.IOrderedEnumerable`1<!!0> [System.Linq]System.Linq.Enumerable::OrderBy<valuetype MyType, int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Runtime]System.Func`2<!!0, !!1>)
ret
© www.soinside.com 2019 - 2024. All rights reserved.