一般来说,托管代码不应该导致访问冲突. 这在本地代码或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. 我很想知道为什么,以及我如何解决它.

推荐答案

你的IL有很多问题.

您已经定义了一个实例字段.

var field = typeBuilder.DefineField("field", typeof(int), FieldAttributes.Public);

然后你定义了一个static函数,所以没有this.

var extractor = typeBuilder.DefineMethod("GetField",
    MethodAttributes.Private | MethodAttributes.Static,
    typeof(int), [typeof(int)]);

所以Ldarg_0返回第一个参数,即int.为什么需要这个参数还不清楚,但是如果你有一个实例字段,你需要一个实例来处理.或者将方法定义为实例方法,或者添加typeBuilder类型的参数,或者将字段定义为static.

var extractor = typeBuilder.DefineMethod("GetField",
    MethodAttributes.Private | MethodAttributes.Static,
    typeof(int), [typeBuilder]);

var extractor = typeBuilder.DefineMethod("GetField",
    MethodAttributes.Private | MethodAttributes.Instance,
    typeof(int), new[]{});

ldtokenGetMethodFromHandle在这里做什么,真的不清楚.ldtoken是用于加载TypeHandle以获得Type对象的意思.您需要ldftn,并且需要使用newobj创建实际的委托.您还定义了一个返回类型,当您不返回任何东西时.

相反,你需要:

var enumType = typeof(IEnumerable<>).MakeGenericType(typeBuilder);
var orderFunc = typeof(Enumerable).GetMethods()
    .Single(m => m.Name == "或derBy" && m.GetParameters().Length == 2)
    .MakeGenericMethod(typeBuilder, typeof(int));
var funcType = orderFunc.GetParameters()[1].ParameterType;

var impl = typeBuilder.DefineMethod("Sort", MethodAttributes.Public | MethodAttributes.Static, enumType, [enumType]);
il = impl.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldftn, extractor);
il.Emit(OpCodes.Newobj, funcType);
il.Emit(OpCodes.Call, orderFunc); // call Enumerable.或derBy(enumerable, extractor)
il.Emit(OpCodes.Ret);

您可能希望在静态字段中缓存funcType委托,如@ JeremyLakeman所示,因为它是一个开放的委托(没有实例,也没有捕获).

Csharp相关问答推荐

CsWin32如何创建PWSTR的实例,例如GetWindowText

如何修改中间件或其注册以正确使用作用域服务?

此反射有什么问题.是否发送值转换委托?

.NET HttpClient、JsonSerializer或误用的Stream中的内存泄漏?

如何解决提交按钮后 Select 选项错误空参照异常

Unix上的.NET(核心):.NET意外地未看到通过P/Invoke系统调用对环境变量进行的进程内修改

为基本审计设置Audit.EntityFramework.Core

使用Dapper映射联接查询对象数据到使用SplitOn;

如何在C#中创建VS代码中的控制台应用程序时自动生成Main方法

EFR32BG22 BLE在SPP模式下与PC(Windows 10)不连接

EF核心区分大小写的主键

C#LINQ子字符串

在扩展方法中,IEnumerable<;T>;不会转换为IEumerable<;T&>

这是否比决定是否使用ConfigureAWait(False)更好?

如何在使用属性 Select 器时判断是否可以为空

从HTML元素获取 colored颜色

如何在C#.NET桌面应用程序中动态更改焦点工具上的后退 colored颜色

当我在Git中暂存文件更改时,它们会消失

为什么我不能在固定语句中使用外部函数?

C#、Visual Studio代码、调试器、错误处理变量请求.未知错误:0x80131502,